ModularFit
Fila de construcao verificavel por fase, com a Fase 0 de Planejamento concluida na aprovacao do DRS.
ModularFit: Roadmap de Implementação
| Campo | Valor |
|---|---|
| Produto | ModularFit |
| Documento base | DRS do ModularFit |
| Companheiros | Inventário de Artefatos · Jornadas dos Atores · Plano de Testes |
| Estágio | Planejamento fechado, mockup aprovado, construção iniciando |
| Data | 2026-09-05 |
Este documento é a fila de construção do ModularFit. Cada tarefa é um cartão com o requisito que ela cumpre, o que implementar, como verificar e o critério de aceite. A verificação vem antes da construção: primeiro se declara o observável que prova o resultado, depois se constrói até ele aparecer.
Como usar
- Marque a tarefa apenas quando o critério de aceite passar na aplicação real, nunca quando o código foi escrito.
- A ordem das fases é dependência técnica e, dentro dela, o foco do produto: o núcleo é o treino de musculação, então a fase de treino vem imediatamente depois de catálogo e vínculo, antes de dieta e de saúde. Uma fase não começa com a anterior aberta.
- Todo requisito do DRS é coberto por ao menos uma tarefa. Se algo precisar ser construído e não estiver aqui, o DRS é atualizado antes do código.
- O progresso do projeto tem três seções: Planejamento (40 por cento), Construção (40 por cento) e Testes (20 por cento). O Planejamento já está concluído na aprovação do documento.
- A seção Construção é todo card que não é de Planejamento, o que inclui a Fase M e as ondas de correção, e não apenas as fases numeradas. É assim que a barra publicada conta, e a prosa deste documento conta igual desde 15/09/2026.
Mapa de cobertura
| Fase | Módulo do DRS | Requisitos cobertos |
|---|---|---|
| Planejamento | todos | elicitação, especificação, companheiros, validação, aprovação |
| M, Mockup Navegável | todos | referência visual do sistema completo |
| 1, Fundação e acesso | core, plan | RF-CORE-001 a 012, RF-PLAN-001 a 004 |
| 2, Catálogo e vínculo | exer, vinc | RF-EXER-001 a 004, RF-VINC-001 a 005 |
| AUD, correções da Inspeção | core, plan, pain, trei, diet | RF-CORE-008, RF-CORE-009, RF-CORE-010, RF-CORE-014, RF-PLAN-001, RF-PLAN-002, RF-PAIN-009, RF-PAIN-011, RF-TREI-001, RF-TREI-004, RF-TREI-009, RF-DIET-001 a 003 |
| 3, Treino | trei | RF-TREI-001 a 010 |
| 4, Dieta | diet | RF-DIET-001 a 005 |
| 5, Saúde e evolução | saud | RF-SAUD-001 a 010 |
| 6, Comunicação | comu | RF-COMU-001 a 006 |
| 7, Engajamento e painéis | enga, pain | RF-ENGA-001 a 006, RF-PAIN-001 a 010 |
| 8, Cobrança, geração e integrações | cobr, iage, inte, exer | RF-COBR-001 a 004, RF-IAGE-001 a 003, RF-EXER-005, RF-INTE-001 a 005 |
| 9, Guia da plataforma | ajud | RF-AJUD-001 a 003 |
Estado global
Atualização de 13/09/2026. Planejamento fechado (28 de 28, 100 por cento). {F1-19} entregue e em produção (635dda0, migração 0018_perfil_publico.sql): tipo composto mfit.perfil_publico_reduzido e função mfit.perfil_publico, únicas portas de leitura reduzida de terceiro; RLS ligada em mfit.usuario, mfit.perfil_aluno e mfit.perfil_profissional, com 8 políticas mais 3 REVOKE DELETE; cinco funções SECURITY DEFINER estreitas mais duas promoções de claim, resolvendo os nove caminhos sem claim; rota GET /api/perfil/:usuarioId com o ramo do Administrador auditado; e o quarto placar de ops/verifica-acesso-aluno.mjs, cujo critério (d) herdado de {F1-14} sobe de 3 servidas + 17 pendentes = 20 para 4 servidas + 17 pendentes = 21. Placar N de 36 de {TA-06} inalterado (as três tabelas de perfil não estão entre as dezoito entidades de E.1). A Fase 1 fecha em código com {F1-19}: os dezenove cards {F1-01} a {F1-19} estão todos entregues. Conferindo a Definição de Pronto da Fase 1 cláusula por cláusula: (1) os trinta e seis casos de isolamento passam, ABERTO, {TA-06} continua [ ] até a décima oitava entidade nascer na Fase 8; (2) o perfil de terceiro devolve apenas os campos públicos, FECHADO por {F1-19}; (3) nenhuma escrita de categoria sensível acontece sem consentimento, FECHADO o mecanismo desde {F1-12}, {TA-10} placar 0 de 6, sobe por entidade sensível na Fase 5; (4) o aviso de natureza não clínica está nos quatro pontos, ABERTO, {TA-85} placar 2 de 4, os outros dois pontos nascem em {F5-01}, {F5-04}, {F5-05}, {F5-06} e {F8-09}; (5) toda escrita produz auditoria, FECHADO desde {F1-06} e {F1-11}; (6) o plano governa os limites por configuração, FECHADO desde {F1-13} e {F1-15}. Logo a Fase 1 fecha em código, e três das seis cláusulas da Definição de Pronto seguem abertas por card dono futuro fora da Fase 1: {TA-06} (Fase 8), {TA-08} (placar 1 de 11, Fase 8), {TA-20} (1 de 2 pontos de exibição, {F3-05} pendente) e {TA-85} (2 de 4, Fases 5 e 8); a semente de exercícios do dono ({ART-WF-17}, docs/seed/exercicios.csv) continua pendente do lado dele, fora do escopo de código. {F2-01} entregue e em produção (merge f3addf5, migração 0019_exercicio.sql): tabela mfit.exercicio com os treze campos, três enums canônicos, RLS com quatro políticas, três gatilhos de auditoria reaproveitados, índice GIN por trigrama e importador idempotente (ops/importar-exercicios.mjs); décimo quarto verificador (ops/verifica-exercicios.mjs); telas /admin/exercicios, /exercicios e /exercicios/:id no ar. A Fase 2 abre. Placar honesto da semente: 0 de 113 exercícios importados nesta data (pendência dele, não bloqueia o card), corrigido em 14/09/2026 para 113 de 113, ver Nota do card. {TA-20} segue aberto (1 de 2 pontos de exibição, execução da sessão pendente em {F3-05}). {F2-02} entregue e em produção (merge 9d326d0, migração 0020_midia_exercicio.sql): middleware único de cabeçalhos de segurança, imagem por arquivo ou endereço, vídeo por endereço de lista fechada, dimensão natural medida no servidor, décimo quinto verificador com navegador (tolerância 0,5 por cento). {ART-FORM-02} fecha, 13 de 13 campos. Placar honesto: 1 de 2 pontos de exibição de mídia ({F3-05} pendente), 1 de 2 repositórios com escrita, 27 parâmetros de operação, semente de mídia do dono ausente (0 de N). {TA-21} segue aberto. {F2-03} entregue e em produção (merge 05d52cf, sem migração): quatro filtros combináveis (grupo, dificuldade, categoria, equipamento) na mesma rota e consulta de {F2-01}, contador único vindo da mesma janela, índice existente medido por EXPLAIN ANALYZE e reaproveitado sem índice novo, verificador 15 de 15, dois drifts de paginação e de contagem corrigidos. {TA-22} fecha. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 24 de 96 = 25 por cento (peso 40), conta: 23 do estado anterior mais 1 de {F2-03} igual 24; Testes 10 de 153 = 6,54 por cento (peso 20), conta: 9 do estado anterior mais 1 de {TA-22} igual 10. Geral = 0,40 x 100 + 0,40 x 25 + 0,20 x 6,54 = 40 + 10 + 1,31 = 51,31 por cento, arredondado para 51 por cento. Atualização de 14/09/2026. {F2-04} entregue e em produção (merge 98db15a, https://fit.modulareasy.com): três pontos de entrada por interface para o catálogo, o link Exercícios nos painéis do aluno, do profissional e do administrador, único gap funcional medido (a tela /exercicios já existia desde {F2-01}); décimo quarto verificador com (j) 0 de 6 dimensões de plano tocando o catálogo (ausência gravada como medida) e (k) 5 de 5 rotas administrativas com papel conferido antes do banco; décimo quinto verificador provando com sessão real de aluno que a navegação em /exercicios e no detalhe não expõe controle de escrita. {TA-23} fecha. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 25 de 96 = 26,04 por cento (peso 40), conta: 24 do estado anterior mais 1 de {F2-04} igual 25; Testes 11 de 153 = 7,19 por cento (peso 20), conta: 10 do estado anterior mais 1 de {TA-23} igual 11. Geral = 0,40 x 100 + 0,40 x 26,04 + 0,20 x 7,19 = 40 + 10,42 + 1,44 = 51,86 por cento, arredondado para 52 por cento. Atualização de 14/09/2026. {F2-05} entregue e em produção (merge 7eaa87b, migração 0021_convite_de_vinculo.sql, https://fit.modulareasy.com): política vinculo_leitura passa a "meus vínculos" (perda declarada, profissional com vínculo ativo deixa de ler vínculos do aluno com terceiros); função definer mfit.usuario_por_identificador_publico resolve o identificador por igualdade exata; gatilho AFTER INSERT ON mfit.vinculo enfileira a notificação por e-mail na origem; POST/GET /api/vinculos e tela /alunos mínima no ar. Décimo sexto verificador com quatro placares: 3 de 3 políticas, 1 de 6 transições servidas, 27 de 27 pontos notificam por construção, pendente 0 linha e ativo 1 linha. {TA-11} fecha. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 26 de 96 = 27,08 por cento (peso 40), conta: 25 do estado anterior mais 1 de {F2-05} igual 26; Testes 12 de 153 = 7,84 por cento (peso 20), conta: 11 do estado anterior mais 1 de {TA-11} igual 12. Geral = 0,40 x 100 + 0,40 x 27,08 + 0,20 x 7,84 = 40 + 10,83 + 1,57 = 52,40 por cento, arredondado para 52 por cento. Atualização de 14/09/2026. {F2-06} entregue e em produção (merge 9b994b0, migração 0022_aceite_e_recusa_do_vinculo.sql, https://fit.modulareasy.com): duas transições novas do aluno por UPDATE condicional dentro de SAVEPOINT (pendente para ativo, pendente para recusado), guardadas por aluno_id = claims.sub AND estado = 'pendente' no próprio WHERE; notificação ao profissional por gatilho na origem; auditoria da transição por critério, ação alterar; GET /api/vinculos passa a servir os dois papéis (conserto, o aluno recebia lista vazia com 200); bloco Vínculos nasce em /perfil (tela 23), sem rota /vinculos nova. Placar de transições da máquina de estados: 4 de 6 servidas. {TA-12} e {TA-18} fecham, o segundo com a jornada completa do limite. pendente para cancelado e a transição proibida hoje alcançável pela rota de desativar seguem nomeadas para {F2-07}. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 27 de 96 = 28,12 por cento (peso 40), conta: 26 do estado anterior mais 1 de {F2-06} igual 27; Testes 14 de 153 = 9,15 por cento (peso 20), conta: 12 do estado anterior mais 2 de {TA-12} e {TA-18} igual 14. Geral = 0,40 x 100 + 0,40 x 28,12 + 0,20 x 9,15 = 40 + 11,25 + 1,83 = 53,08 por cento, arredondado para 53 por cento. Atualização de 14/09/2026. {F2-07} entregue e em produção (merge 42cd973, migração 0023_encerramento_do_vinculo.sql, https://fit.modulareasy.com): POST /api/vinculos/:id/desativar consertado (WHERE id = $1 AND estado = 'ativo'), quinto desfecho 409 estado_invalido para vínculo fora de ativo, e a capacidade do Administrador de terminar um convite pendente devolvida pela rota nova POST /api/vinculos/:id/cancelar; gatilho BEFORE UPDATE OF estado (mfit.validar_transicao_vinculo) recusa por construção as duas transições proibidas de E.3, P0001 na origem; gatilho AFTER UPDATE OF estado (mfit.enfileirar_saida_de_vinculo, SECURITY DEFINER) notifica as duas partes no encerramento e o aluno no cancelamento, modelos vinculo_encerrado e convite_cancelado sem token; auditoria sem gatilho novo, encerramento continua desativar e cancelamento cai em alterar; gabarito GABARITO-ACESSO-ALUNO.md ganha a separação de autoria na leitura (pode_ler_aluno(aluno_id, autor_id)) e na escrita (pode_ler_aluno(aluno_id) sem o segundo argumento), critério (e) novo de ops/verifica-acesso-aluno.mjs reprovando política de escrita que passe autor_id, placar de partida 0 de 0 tabelas com autor_id existem hoje, 11 pendentes nomeadas; décimo sexto verificador fecha o placar de transições em 6 de 6 e o de notificação em 6 de 6. Arnês com banco real (encerramento.test.ts, 22 casos) prova o H1 pelos dois lados, aluno e profissional, com tabela de ensaio mfit.ficha_ensaio: as três fichas legíveis pelo próprio profissional após o encerramento (condição 3 de D.2), a quarta ficha de autoria alheia invisível a ele, e INSERT/UPDATE negados com 403. {TA-13} fecha, os dois lados provados. /alunos ganha Encerrar na linha ativa e Cancelar na pendente; /perfil ganha Encerrar no vínculo ativo. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 28 de 96 = 29,17 por cento (peso 40), conta: 27 do estado anterior mais 1 de {F2-07} igual 28; Testes 15 de 153 = 9,80 por cento (peso 20), conta: 14 do estado anterior mais 1 de {TA-13} igual 15. Geral = 0,40 x 100 + 0,40 x 29,17 + 0,20 x 9,80 = 40 + 11,67 + 1,96 = 53,63 por cento, arredondado para 54 por cento. Atualização de 14/09/2026. {F2-08} entregue e em produção (merge 25e96ad, https://fit.modulareasy.com): nenhuma migração nova, última continua 0023; card de prova, sem código de produto, critério (e) novo em verificarVinculos.ts mais placar em ops/verifica-vinculo.mjs (universo de catálogo, decoy de seis trechos). Placar de ausência: 0 de 15 objetos de mfit.vinculo usam especialidade como guarda, 0 de 5 caminhos de escrita aplicam guarda por especialidade. Placar positivo: 4 de 4 especialidades, 2 personal_trainer no mesmo aluno, 5 vínculos ativos, contagem do limite igual a 5; teto de exibição medido, 50 linhas por consulta, sem paginação (Drift 3, dono {F7-07}). Arnês simultaneos.test.ts (16 casos), rotas reais. {TA-14} fecha com as quatro metades nominadas. Décimo sexto verificador: placar de transições inalterado, 6 de 6. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 29 de 96 = 30,21 por cento (peso 40), conta: 28 do estado anterior mais 1 de {F2-08} igual 29; Testes 16 de 153 = 10,46 por cento (peso 20), conta: 15 do estado anterior mais 1 de {TA-14} igual 16. Geral = 0,40 x 100 + 0,40 x 30,21 + 0,20 x 10,46 = 40 + 12,08 + 2,09 = 54,17 por cento, arredondado para 54 por cento. Atualização de 14/09/2026. {F2-09} entregue e em produção (merge 0668358, migração 0024_gestao_global_de_vinculos.sql, https://fit.modulareasy.com): listagem global paginada (GET /api/admin/vinculos) com os quatro filtros e nomes por LEFT JOIN LATERAL mfit.perfil_publico; tela /admin/vinculos com Desativar, Cancelar e Excluir, sem Ativar e sem Novo vínculo; GET /api/vinculos passa a recusar o Administrador com 403, perda declarada e sem consumidor. {TA-15} fecha com as duas metades nominadas. Fase 2 fecha: catálogo global lido por todos ({F2-01} a {F2-04}), vínculo governa o acesso do profissional ({F2-05} a {F2-07} e {F2-09} pelo caminho do Administrador), aluno acumula vínculos sem limite por especialidade ({F2-08}), máquina de estados recusa as transições proibidas ({F2-07}). Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 30 de 96 = 31,25 por cento (peso 40), conta: 29 do estado anterior mais 1 de {F2-09} igual 30; Testes 17 de 153 = 11,11 por cento (peso 20), conta: 16 do estado anterior mais 1 de {TA-15} igual 17. Geral = 0,40 x 100 + 0,40 x 31,25 + 0,20 x 11,11 = 40 + 12,50 + 2,22 = 54,72 por cento, arredondado para 55 por cento. Atualização de 14/09/2026. {F3-01} entregue e em produção (merge 17bd22e, migração 0025_ficha_de_treino.sql, https://fit.modulareasy.com): abre a Fase 3, ficha de treino criada pelo profissional com autoria por linha e por leitura, desativação por status, limite de plano por dimensão e três telas com componente único do item. {TA-24} continua [ ], provado pela metade, a Nota declara qual (Drift 4). Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 31 de 96 = 32,29 por cento (peso 40), conta: 30 do estado anterior mais 1 de {F3-01} igual 31; Testes 17 de 153 = 11,11 por cento (peso 20), inalterado, {TA-24} não fecha inteiro. Geral = 0,40 x 100 + 0,40 x 32,29 + 0,20 x 11,11 = 40 + 12,92 + 2,22 = 55,14 por cento, arredondado para 55 por cento. Atualização de 14/09/2026. {F3-02} entregue e em produção (merge 3d25e27, https://fit.modulareasy.com, sem migração, última em produção continua 0025): o aluno sem vínculo passa a criar a própria ficha pela mesma tela do profissional, guarda de papel fechado em lista, e o profissional lê a ficha do aluno pela mesma /treinos com ?aluno=; rótulo de autoria unificado num ponto único que decide pelo leitor antes da linha, décimo sétimo verificador com decoy. {TA-25} fecha inteiro, as duas metades do H1 provadas pela interface real (criar sem vínculo, depois vincular e ler identificada como do aluno). Aritmética recalculada pela contagem real de [x] no documento publicado: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 32 de 96 = 33,33 por cento (peso 40), conta: 31 do estado anterior (29 cards de fase mais 2 de Fase M) mais 1 de {F3-02} igual 32; Testes 18 de 153 = 11,76 por cento (peso 20), conta: 17 do estado anterior mais 1 de {TA-25} igual 18. Geral = 0,40 x 100 + 0,40 x 33,33 + 0,20 x 11,76 = 40 + 13,33 + 2,35 = 55,68 por cento, arredondado para 56 por cento. Atualização de 14/09/2026. {F3-03} entregue e em produção (merge 76bc9cd, migração 0026_edicao_da_ficha.sql, https://fit.modulareasy.com): edição da ficha pelo autor e pelo Administrador via PATCH /api/fichas/:id numa transação única, reconciliando a lista de itens por identificador; auxiliar mfit.pode_editar_por_autoria liberando o Administrador nas políticas de UPDATE e na nova política de DELETE de ficha_item; restrição de ordem única passa a adiável dentro da transação; gatilhos de criação e de alteração instalados nas duas tabelas da ficha, critério de tabela alterável com auditoria de alteração fecha em 7 servidas + 2 declaradas = 9 de 9; desativação e reativação por rotas próprias, fora do PATCH. Perda declarada: o UPDATE do Administrador deixa de ser recusado pela política. {TA-09} fecha inteiro; {TA-26} continua [ ], a metade que falta depende de {F3-09}. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 33 de 96 = 34,38 por cento (peso 40), conta: 32 do estado anterior mais 1 de {F3-03} igual 33; Testes 19 de 153 = 12,42 por cento (peso 20), conta: 18 do estado anterior mais 1 de {TA-09} igual 19. Geral = 0,40 x 100 + 0,40 x 34,38 + 0,20 x 12,42 = 40 + 13,75 + 2,48 = 56,23 por cento, arredondado para 56 por cento. Atualização de 14/09/2026. {F3-04} entregue e em produção (merge 8f0f9fd, migração 0027_solicitacao_de_ficha.sql, https://fit.modulareasy.com): solicitação de ficha nova ou de ajuste pelo aluno, com atendimento ou recusa pelo profissional destinatário, três avisos por gatilho na origem cobrindo por transição os dois caminhos de atendimento, e acompanhamento mínimo do próprio pedido pelo aluno. {TA-27} fecha inteiro; {TA-26} continua [ ], a metade que falta depende de {F3-09}. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 34 de 96 = 35,42 por cento (peso 40), conta: 33 do estado anterior mais 1 de {F3-04} igual 34; Testes 20 de 153 = 13,07 por cento (peso 20), conta: 19 do estado anterior mais 1 de {TA-27} igual 20. Geral = 0,40 x 100 + 0,40 x 35,42 + 0,20 x 13,07 = 40 + 14,17 + 2,61 = 56,78 por cento, arredondado para 57 por cento. Atualização de 2026-09-15. {AUD-03} entregue e em produção (merge 8daaf7d, sem migração, backup lógico prévio em /opt/modularfit/backups/): duas contas de teste da Inspeção removidas em definitivo (inspecao-qa-20260906@ e inspecao-visual-20260907@), auditoria ganhando duas entradas excluir (origem ops/limpar-contas-de-teste) e nenhuma linha órfã em vínculo, consentimento, ficha ou e-mail; segunda rodada removeu zero. Onda AUD fica fora do denominador de 96 da Construção, por não ser uma das nove fases do Mapa de cobertura: Planejamento 28 de 28 = 100 por cento (peso 40); Construção permanece 34 de 96 = 35,42 por cento (peso 40); Testes permanece 20 de 153 = 13,07 por cento (peso 20). Geral permanece 0,40 x 100 + 0,40 x 35,42 + 0,20 x 13,07 = 40 + 14,17 + 2,61 = 56,78 por cento, arredondado para 57 por cento, inalterado. Atualização de 2026-09-15. {F3-05} entregue e em produção (merge 80c103e, migração 0028_execucao_da_sessao.sql, https://fit.modulareasy.com): execução da sessão de treino inteira, duas tabelas novas, décimo oitavo verificador, rota própria /treinos/sessao; {TA-28}, {TA-24} e {TA-20} fecham, {TA-21} continua aberto por ausência de mídia no catálogo (docs/seed/midia/, pendência do dono). Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 35 de 96 = 36,46 por cento (peso 40), conta: 34 do estado anterior mais 1 de {F3-05} igual 35; Testes 23 de 153 = 15,03 por cento (peso 20), conta: 20 do estado anterior mais 3 de {TA-28}, {TA-24} e {TA-20} igual 23. Geral = 0,40 x 100 + 0,40 x 36,46 + 0,20 x 15,03 = 40 + 14,58 + 3,01 = 57,59 por cento, arredondado para 58 por cento. Atualização de 2026-09-15. {F3-06} entregue e em produção (merge f21943f, sem migração, https://fit.modulareasy.com): cronômetro de descanso inteiramente web, fonte da verdade pelo instante de fim (nunca contador decrementado), aviso sonoro por Web Audio sem asset novo, ajuste +15 s e botão de pular, zero coluna, zero rota e zero migração novas. {TA-29} continua [ ], provada pela metade (a Nota declara qual). Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 36 de 96 = 37,50 por cento (peso 40), conta: 35 do estado anterior mais 1 de {F3-06} igual 36; Testes 23 de 153 = 15,03 por cento (peso 20), inalterado, {TA-29} não fecha inteiro. Geral = 0,40 x 100 + 0,40 x 37,50 + 0,20 x 15,03 = 40 + 15,00 + 3,01 = 58,01 por cento, arredondado para 58 por cento. Atualização de 2026-09-15. {F3-07} entregue e em produção (merge c331286, sem migração, última em produção continua 0028, https://fit.modulareasy.com, gates plan, qa e review verdes): histórico de sessões em /treinos/historico, bloco de evolução por exercício no resumo da execução e carga anterior durante a execução, os três pontos de entrega do card, com a progressão derivada na leitura em ponto único. {TA-30} continua [ ]: três das cinco cláusulas do Passa quando fecharam com dado real no smoke (variação exata, primeira medição, exercício só por tempo sem chip), a leitura pelo profissional vinculado e a conferência linha a linha de data e ficha por sessão não têm evidência no gate de qa. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 37 de 96 = 38,54 por cento (peso 40), conta: 36 do estado anterior mais 1 de {F3-07} igual 37; Testes 23 de 153 = 15,03 por cento (peso 20), inalterado, {TA-30} não fecha inteiro. Geral = 0,40 x 100 + 0,40 x 38,54 + 0,20 x 15,03 = 40 + 15,42 + 3,01 = 58,43 por cento, arredondado para 58 por cento.
Atualização de 2026-09-15. {F3-08} entregue e em produção (merge ce1787b, migração 0029_preferencia_treino.sql, https://fit.modulareasy.com, gates plan, db, qa e review verdes): preferências de treino do aluno, sete campos, três listas fechadas (oito objetivos, três níveis, vinte e seis equipamentos), tela /treinos/preferencias nos dois modos (edição do aluno, leitura somente do profissional vinculado) e ponto de entrada no cabeçalho de /treinos. {TA-31} fecha inteiro: as cinco cláusulas do Passa quando (dois objetivos, nível intermediário, cinco equipamentos, três dias, sessenta minutos) provadas ao vivo com screenshot batendo os valores salvos, mais a leitura do profissional vinculado confirmada na mesma rodada de qa. {TA-06} permanece [ ]: o placar do arnês de isolamento não foi impresso em nenhum dos gates desta rodada (plan, db, qa, review), Nota registrada no Plano de Testes nomeando a lacuna, sem número presumido. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 38 de 96 = 39,58 por cento (peso 40), conta: 37 do estado anterior mais 1 de {F3-08} igual 38; Testes 23 de 153 = 15,03 por cento (peso 20), inalterado: {TA-31} não fecha nesta rodada, o gate de qa (rodada 2, PASS) prova duas das cinco cláusulas acrescentadas pelo Drift 6 (a recusa por campo de objetivo e nível ausentes, e a ausência de leitura ao profissional sem vínculo), e não prova as outras três (os dois textos de até 2000 caracteres idênticos com o teto recusado, o valor fora da lista fechada e a duração fora da faixa ou do passo recusados, e desmarcar tudo aceito com reabertura vazia), Nota nominal no Plano de Testes nomeando a lacuna. Geral = 0,40 x 100 + 0,40 x 39,58 + 0,20 x 15,03 = 40 + 15,83 + 3,01 = 58,84 por cento, arredondado para 59 por cento, inalterado pelo arredondamento.
Atualização de 2026-09-15, escada de acesso. A Onda AUD ganha dois cards, {AUD-04} (papel Super Administrador separado do Administrador) e {AUD-05} (personificação com registro), os dois autorados abertos, nenhum entregue nesta data. O documento de requisitos foi republicado junto: RF-CORE-014 novo, RN-010 nova, ADR-017 novo, e os blocos B.1, B.4, B.5, B.6, D.2, D.5, E.1, C.1, o Apêndice de matriz e o Apêndice de catálogo de telas reconciliados. O total de cards deste roadmap passa de 125 para 127.
Reconciliação do denominador da Construção, e ela corrige um número que a prosa vinha declarando errado. As atualizações anteriores escreviam "Construção 38 de 96", contando as nove fases mais a Fase M e deixando a Onda AUD de fora. A barra publicada nunca contou assim: ela separa as duas seções pelo prefixo do código, Planejamento é o que começa por P0 e Construção é todo o resto, de modo que a Onda AUD sempre esteve dentro do denominador dela. Medido hoje pelo próprio serviço de estado do checklist: 125 cards, 28 de Planejamento e 97 de Construção, com 39 de Construção marcados (36 de fase, 2 da Fase M e {AUD-03}). Logo a prosa dizia 38 de 96, 39,58 por cento, e a barra mostrava 39 de 97, 40,21 por cento. A partir desta atualização a prosa passa a declarar o mesmo universo da barra, e quem quiser a leitura por fase continua tendo 38 de 96 nas nove fases mais a Fase M, fora a Onda AUD.
Aritmética desta data, conferida contra a marcação real do publicado. Planejamento 28 de 28 = 100 por cento (peso 40); Construção 39 de 99 = 39,39 por cento (peso 40), conta: 97 cards do estado anterior mais {AUD-04} e {AUD-05} igual a 99, marcados os mesmos 39; Testes 23 de 157 = 14,65 por cento (peso 20), conta: 153 cards do estado anterior mais {TA-90}, {TA-91}, {TA-92} e {TB-54} igual a 157, marcados os mesmos 23. Geral = 0,40 x 100 + 0,40 x 39,39 + 0,20 x 14,65 = 40 + 15,76 + 2,93 = 58,69 por cento, arredondado para 59 por cento. O geral não sobe porque nada foi entregue nesta data: o que mudou foi o universo, e universo que cresce sem entrega faz a barra descer, o que é o comportamento correto e se declara em vez de se esconder.
Atualização de 2026-09-15, fechamento de {F3-09}. Duplicação de ficha de terceiro como ficha própria entregue e em produção (merge e931e3b, migração 0030_duplicacao_da_ficha.sql, https://fit.modulareasy.com, gates plan, db, qa e review verdes). Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 40 de 99 = 40,40 por cento (peso 40), conta: 39 do estado anterior mais 1 de {F3-09} igual 40; Testes 25 de 157 = 15,92 por cento (peso 20), conta: 23 do estado anterior mais {TA-26} e {TA-32} igual 25. Geral = 0,40 x 100 + 0,40 x 40,40 + 0,20 x 15,92 = 40 + 16,16 + 3,18 = 59,34 por cento, arredondado para 59 por cento.
Atualização de 2026-09-29, fechamento de {AUD-05}. Personificação com registro entregue e em produção (merge 3dabc30, https://fit.modulareasy.com, migrações 0034, 0035 e 0036, gates plan, qa (rodada 2, PASS COM RESSALVAS) e review (APPROVED) verdes). O Super Administrador personifica um usuário a partir de /admin/usuarios, a faixa aparece em toda tela com o tempo vindo do servidor, a trilha ganha as entradas de início e de fim com o par personificador e personificado, e a saída, explícita ou por expiração, devolve a sessão do topo sem tela de entrada. Fica a ressalva nominal do qa: a tela /admin/usuarios nasceu mínima, com a busca e uma única ação, e só fecha como tela de gestão de usuários em {F7-14}; a tela de edição dos parâmetros personificacao_duracao_min e personificacao_aviso_min é de {AUD-06}, e hoje os dois valem como valor vivo em server/src/config.ts. Aritmética medida no publicado antes do flip: 130 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 41 de 102 = 40,20 por cento, e depois do flip 42 de 102 = 41,18 por cento (peso 40), conta: 41 marcados mais 1 de {AUD-05} igual 42; Testes 25 de 160 = 15,63 por cento, e depois do flip 26 de 160 = 16,25 por cento (peso 20), conta: 25 marcados mais {TA-91} igual 26, com {TB-54} aberto por ser Beta com operador real. Geral = 0,40 x 100 + 0,40 x 41,18 + 0,20 x 16,25 = 40 + 16,47 + 3,25 = 59,72 por cento, arredondado para 60 por cento. Os números de partida do gate (40 de 99 e 23 de 157) estavam velhos em relação ao publicado (130 cards, 41 de 102 e 25 de 160, por causa de {F3-09}, {AUD-06} a {AUD-08} e {TA-93} a {TB-55}), e a prosa declara os medidos.
Atualização de 2026-09-15, fechamento de {AUD-04}. Papel Super Administrador separado do Administrador entregue e em produção (merge 9aa10db, migrações 0031_papel_super_administrador.sql, 0032_escada_de_acesso.sql e 0033_promocao_das_contas_de_topo.sql, https://fit.modulareasy.com, gates plan, db, qa e review verdes). Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 41 de 99 = 41,41 por cento (peso 40), conta: 40 do estado anterior mais 1 de {AUD-04} igual 41; Testes permanece 25 de 157 = 15,92 por cento (peso 20), {TA-90} e {TA-92} não fecham nesta data, placares parciais nomeados no Plano de Testes (governança 4 de 7, lado positivo 7 de 10, fronteira 0 de 28). Geral = 0,40 x 100 + 0,40 x 41,41 + 0,20 x 15,92 = 40 + 16,56 + 3,18 = 59,74 por cento, arredondado para 60 por cento.
Atualização de 2026-09-29, fechamento de {AUD-06}. Parâmetros de operação editáveis entregues e em produção (merge 29380ae, https://fit.modulareasy.com, migrações 0037 e 0038, gates plan, qa (PASS COM RESSALVAS) e review (APPROVED na rodada 4) verdes): 28 linhas em mfit.parametro, ponto único de leitura, tela /admin/parametros e verificador com placar 79 de 101 (universo 148). A prosa do card, que dizia 100 pontos editáveis, passa a dizer 101. Aritmética medida no publicado antes do flip: 130 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 42 de 102 = 41,18 por cento, e depois do flip 43 de 102 = 42,16 por cento (peso 40), conta: 42 marcados mais {AUD-06} igual 43; Testes 26 de 160 = 16,25 por cento, e depois do flip 28 de 160 = 17,50 por cento (peso 20), conta: 26 marcados mais {TA-93} e {TA-94} igual 28, com {TB-55} aberto por ser Beta. Geral = 0,40 x 100 + 0,40 x 42,16 + 0,20 x 17,50 = 40 + 16,86 + 3,50 = 60,36 por cento, arredondado para 60 por cento.
Atualização de 2026-09-29, fechamento de {AUD-07}. Listas fechadas do catálogo migradas para a tabela editável, entregue e em produção (merge 7b47fa6, https://fit.modulareasy.com, migrações 0039 e 0040, gates plan, qa e review verdes): 52 itens em mfit.lista_fechada_item, os seis tipos enum removidos, tela /admin/listas e verificador com placar 70 de 100 (universo 147). Aritmética medida no publicado antes do flip: 130 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 43 de 102 = 42,16 por cento, e depois do flip 44 de 102 = 43,14 por cento (peso 40), conta: 43 marcados mais {AUD-07} igual 44; Testes 28 de 163 = 17,18 por cento, e depois do flip 29 de 163 = 17,79 por cento (peso 20), conta: 28 marcados mais {TA-95} igual 29, com {TB-56} aberto por ser Beta. Geral = 0,40 x 100 + 0,40 x 43,14 + 0,20 x 17,79 = 40 + 17,25 + 3,56 = 60,81 por cento, arredondado para 61 por cento.
Atualização de 2026-09-29, fechamento de {AUD-08}. Valores de negócio fora do catálogo entram no catálogo, entregue e em produção (merge fa725bc, https://fit.modulareasy.com, migração 0041, gates plan, qa (PASS COM RESSALVAS) e review (APPROVED) verdes): 14 linhas novas em mfit.parametro (42 no total), config.ts de 26 para 11 chaves, forma texto editável na tela de parâmetros e verificador com placar 52 de 98 (universo 146, 48 exceções C). A Onda AUD fecha inteira com este card: {AUD-03} a {AUD-08} entregues, e {AUD-01} e {AUD-02} já tinham entrado corrigidos pela migração 0025, sem card. Sem onda de correção aberta, o que resta na fila é a Fase 3 a partir de {F3-10}. Aritmética medida no publicado antes do flip: 130 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 44 de 102 = 43,14 por cento, e depois do flip 45 de 102 = 44,12 por cento (peso 40), conta: 44 marcados mais {AUD-08} igual 45; Testes 29 de 163 = 17,79 por cento, e depois do flip 30 de 163 = 18,40 por cento (peso 20), conta: 29 marcados mais {TA-96} igual 30, com {TB-55} aberto por ser Beta. Geral = 0,40 x 100 + 0,40 x 44,12 + 0,20 x 18,40 = 40 + 17,65 + 3,68 = 61,33 por cento, arredondado para 61 por cento.
Atualização de 2026-09-29, fechamento de {F3-10}. Retomada de sessão em andamento e regra de abandono, entregue e em produção (merge 047f3bc, https://fit.modulareasy.com, migração 0042, gates plan, qa (PASS COM RESSALVAS) e review (APPROVED) verdes): atalho de retomar no painel e em /treinos, prazo de abandono lido de sessao_abandono_horas, passagem a abandonada por escritor único com rotina horária para quem nunca volta, e placar de transições da sessão em 5 de 5. A Fase 3 fecha com este card: {F3-01} a {F3-10} entregues. Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 130 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 45 de 102 = 44,12 por cento, e depois do flip 46 de 102 = 45,10 por cento (peso 40), conta: 45 marcados mais {F3-10} igual 46; Testes 33 de 163 = 20,25 por cento, e depois do flip 34 de 163 = 20,86 por cento (peso 20), conta: 33 marcados (30 Alfa e 3 Beta) mais {TA-79} igual 34. Geral = 0,40 x 100 + 0,40 x 45,10 + 0,20 x 20,86 = 40 + 18,04 + 4,17 = 62,21 por cento, arredondado para 62 por cento. O parágrafo anterior citava Testes 30 de 163: a contagem de então não incluía três cartões Beta já marcados, que o endpoint hoje soma.
Atualização de 2026-09-30, fechamento de {F4-01}. Registro de refeição com somatório de macronutrientes entregue e em produção (merge 5e36145, migração 0043, https://fit.modulareasy.com, gates plan, qa (PASS COM RESSALVAS, rodada 2 PASS) e review (APPROVED na rodada 3) verdes): primeira tabela da fronteira de dado clínico, fronteira em 1 de 13, lista tipo_refeicao com 8 itens, 44 parâmetros, placar de parâmetros 52 de 98 com C 51 e universo 149. Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 130 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 46 de 102 = 45,10 por cento, e depois do flip 47 de 102 = 46,08 por cento (peso 40), conta: 46 marcados mais {F4-01} igual 47; Testes 34 de 163 = 20,86 por cento, e depois do flip 35 de 163 = 21,47 por cento (peso 20), conta: 34 marcados mais {TA-33} igual 35. Geral = 0,40 x 100 + 0,40 x 46,08 + 0,20 x 21,47 = 40 + 18,43 + 4,29 = 62,72 por cento, arredondado para 63 por cento. Nenhum card novo nasce: continuam 130 cards no Roadmap e 163 no Plano de Testes.
Atualização de 2026-09-30, fechamento de {F4-02}. Metas nutricionais por aluno entregues e em produção (merge b569460, migração 0044, https://fit.modulareasy.com, gates plan (APPROVED com trava de drift), qa (PASS COM RESSALVAS, reconferido na rodada 2) e review (APPROVED na rodada 2) verdes): tabela meta_nutricional só de acréscimo, bloco "Metas do dia" e formulário "Meta nutricional" em /nutricao, leitura do dia pelo Profissional vinculado em /nutricao?aluno=, mfit.meta_vigente como ponto único da meta do dia e primeiro auxiliar de capacidade por especialidade, fronteira de dado clínico em 2 de 13, placar de parâmetros 52 de 98 com C 53 e universo 151, e metas_padrao com o primeiro consumidor. {TA-34} fica [ ] com 3 de 4 cláusulas provadas; a quarta, a comparação do painel com a meta, é de {F4-04}. Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 131 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 47 de 103 = 45,63 por cento, e depois do flip 48 de 103 = 46,60 por cento (peso 40), conta: 47 marcados mais {F4-02} igual 48; Testes 35 de 164 = 21,34 por cento (peso 20), inalterado, porque {TA-34} continua aberto e {TB-06} é Beta. Geral = 0,40 x 100 + 0,40 x 46,60 + 0,20 x 21,34 = 40 + 18,64 + 4,27 = 62,91 por cento, arredondado para 63 por cento, inalterado pelo arredondamento. Próximo da fila: {AUD-09}, depois {F4-03}. Nenhum card novo nasce: continuam 131 cards no Roadmap e 164 no Plano de Testes.
Atualização de 2026-09-30, fechamento de {AUD-09}. Especialidade aplicada na ficha de treino entregue e em produção (merge d760b2d, migração 0045, https://fit.modulareasy.com, gates plan, db, qa (PASS COM RESSALVAS) e review (APPROVED na rodada 2) verdes): Médico e Nutricionista deixam de criar, duplicar, editar, desativar, reativar, atender e recusar ficha de treino, e o aluno deixa de abrir pedido a eles; verificador de capacidade por especialidade de 1 de 7 para 7 de 7; contagem do Drift 4 zero. Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 133 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 48 de 105 = 45,71 por cento, e depois do flip 49 de 105 = 46,67 por cento (peso 40), conta: 48 marcados mais {AUD-09} igual 49; Testes 35 de 166 = 21,08 por cento, e depois do flip 36 de 166 = 21,69 por cento (peso 20), conta: 35 marcados mais {TA-97} igual 36. Geral = 0,40 x 100 + 0,40 x 46,67 + 0,20 x 21,69 = 40 + 18,67 + 4,34 = 63,01 por cento, arredondado para 63 por cento. Próximo da fila: {AUD-11}, depois {AUD-10} e {F4-03}. Nenhum card novo nasce: continuam 133 cards no Roadmap e 166 no Plano de Testes.
Atualização de 2026-09-15, {AUD-06}. A Onda AUD ganha o quarto card, {AUD-06} (parâmetros de operação editáveis pelo Super Administrador), autorado aberto, reconciliando o Drift 1 do gate modularfit-AUD-06 (dono de {ART-CFG-01} confirmado). O documento de requisitos foi republicado junto: RF-PAIN-011 novo, e o total de RFs do DRS passa de 90 para
- O total de cards deste roadmap passa de 127 para 128, com a Construção passando de 99
para 100 cards, 41 marcados, inalterado. O Plano de Testes ganha os cards {TA-93}, {TA-94} e {TB-55}, passando de 157 para 160 cards, 25 marcados, inalterado. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 41 de 100 = 41,00 por cento (peso 40), inalterada em marcação; Testes 25 de 160 = 15,63 por cento (peso 20), inalterada em marcação. Geral = 0,40 x 100 + 0,40 x 41,00 + 0,20 x 15,63 = 40 + 16,40 + 3,13 = 59,53 por cento, arredondado para 60 por cento, o mesmo arredondamento de antes. O geral não sobe nem desce de fato: o que mudou foi o universo, ampliado pela reconciliação do gate, não a entrega.
Atualização de 2026-09-15, reconciliação {AUD-06} (Passo 1 e 2 do gate). Cinco drifts P0 e um P1 novos, achados só depois do publicado (seção 8.2 do gate modularfit-AUD-06), fecham nesta republicação: B.1 do DRS passa de sete para oito capacidades privativas (manter o catálogo de parâmetros de operação, RF-PAIN-011), B.6 de 41/20/21 para 42/20/22, o Apêndice Matriz Ator por Capacidade de 47 para 48 linhas, e {TA-92} republicado com os números novos e a nota de que a metade documental, fechada em {AUD-04}, reabre e refecha aqui. {ART-CFG-02} nasce no Inventário, o ponto único de leitura que RF-PAIN-011 e {TA-94} já citavam sem ele existir. {ART-CFG-01} corrige a unidade de email_tentativas_recuo (o documento publicava segundos onde o código lê minutos) e de url_assinada_validade (conversão declarada no ponto único), dá faixa verificável às sete células que diziam "livre" ou "conforme operação", tira listas_fechadas e consentimento_textos de mfit.parametro com dono nominal, e ganha a linha personificacao_aviso_min, subindo de 31 para 32 parâmetros. A Onda AUD ganha dois cards, {AUD-07} (listas fechadas do catálogo saem das sete constantes para o ponto único) e {AUD-08} (os dezessete valores de negócio fora do catálogo entram em {ART-CFG-01} ou em mfit.texto_versionado, com destino nomeado um a um), os dois autorados abertos. Nenhum RF novo nasce nesta reconciliação (RF-PAIN-011 já contava desde a republicação anterior; o total de RFs do DRS permanece 91). O total de cards deste roadmap passa de 128 para 130, com a Construção passando de 100 para 102 cards, 41 marcados, inalterado. Aritmética: Planejamento 28 de 28 = 100 por cento (peso 40); Construção 41 de 102 = 40,20 por cento (peso 40), inalterada em marcação; Testes 25 de 160 = 15,63 por cento (peso 20), inalterada em marcação. Geral = 0,40 x 100 + 0,40 x 40,20 + 0,20 x 15,63 = 40 + 16,08 + 3,13 = 59,21 por cento, arredondado para 59 por cento. O geral desce um ponto de arredondamento porque o universo cresceu (dois cards novos na Construção) sem entrega nova, o que é o comportamento correto e se declara em vez de se esconder.
Atualização de 2026-09-29, reconciliação {AUD-07} (gate modularfit-AUD-07, seção 1). Os drifts ratificados pelo Fiscal fecham nesta republicação. São seis as listas fechadas com consumidor (não cinco), e as quatro sem módulo nascem na mesma tabela pelos cards {F4-01}, {F5-02}, {F5-04} e {F5-08}, que ganham uma linha cada. tipoConteudo.ts é reclassificado de B para C e o denominador de editáveis cai de 101 para 100. O H1 e o Como verificar de {AUD-07} passam a dizer que os dois CHECK de teto são derrubados por migração posterior e que o verificador reconhece restrição derrubada. O Plano de Testes ganha {TA-95} e {TB-56}, ambos abertos, e passa de 160 para 162 cards, 28 marcados: Testes 28 de 162 = 17,28 por cento (peso 20). Nenhum card novo nasce neste roadmap: continuam 130 cards, 71 marcados, sem nenhuma marcação alterada.
Atualização de 2026-09-29, Inspeção: card {AUD-09}. Os drifts ratificados do gate modularfit-F4-02 (seção 1) entram como Notas nos cards {F4-02}, {F4-04} e {F4-05}, e o Drift 7 vira um card novo da Onda AUD, {AUD-09} (Especialidade aplicada na ficha de treino: criar, duplicar, atender solicitação), autorado aberto, depois de {AUD-08}. Achado do loop, triado pela Inspeção: a Matriz Ator por Capacidade dá N a Médico e Nutricionista em três capacidades de ficha de treino e, em produção, os dois criam ficha hoje. O card depende de {F4-02}, entra na fila depois dele e antes de {F4-03}, e a Onda AUD, dada por fechada na Atualização de {AUD-08}, reabre com ele. Nenhum RF novo nasce e nenhuma marcação muda. Aritmética medida no publicado antes desta republicação: 130 cards, 75 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 47 de 102 = 46,08 por cento (peso 40); Testes 35 de 163 = 21,47 por cento (peso 20). Depois, com {AUD-09} e {TA-97} abertos: 131 cards, 75 marcados; Construção 47 de 103 = 45,63 por cento, conta: 102 mais {AUD-09} igual 103, marcados inalterados; Testes 35 de 164 = 21,34 por cento, conta: 163 mais {TA-97} igual 164. Geral = 0,40 x 100 + 0,40 x 45,63 + 0,20 x 21,34 = 40 + 18,25 + 4,27 = 62,52 por cento, arredondado para 63 por cento (antes, 62,73, também 63). O geral cai 0,21 ponto porque o universo cresceu (um card em cada seção) sem entrega nova, o que se declara em vez de se esconder.
Atualização de 2026-09-30, Inspeção: cards {AUD-11} e {AUD-10}. Duas decisões de escopo da Inspeção entram como cards novos da Onda AUD, autorados abertos. {AUD-11}: nutrição passa a ser a sétima categoria de consentimento de RF-CORE-010, com RN-001, E.1 e o módulo diet alinhados no DRS, porque o próprio DRS já classifica nutrição como dado clínico na fronteira da administração e a lista de seis categorias ficou incoerente com isso; medido pelo loop em 30/09, refeicao e meta_nutricional estão marcadas não sensíveis no registro de entidades e um Personal Trainer vinculado sem consentimento lê as duas com 200. {AUD-10}: os onze e-mails transacionais passam a ter modelo editável pelo Super Administrador, assunto e corpo com variáveis validadas no servidor, em tabela própria com versões e tela /admin/emails (extensão de RF-PAIN-011), cumprindo a ordem do dono de 15/09 de que toda regra de negócio seja configurável pelo painel de Super Administrador; os onze assuntos de mfit.parametro migram para lá. Ordem da fila da Onda AUD: {AUD-09} (em curso), depois {AUD-11}, depois {AUD-10}, e só então {F4-03}; os cards aparecem nesta ordem no documento, e a fila pega o primeiro aberto da onda. {AUD-11} vem antes de {F4-03} porque plano_alimentar nasce já sob a categoria nutrição. Nenhum RF novo nasce (RF-CORE-010 e RF-PAIN-011 são estendidos) e nenhuma marcação muda. Aritmética medida no publicado antes desta republicação (contagem de cartões no texto, igual à do endpoint de estado): 131 cards, 76 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 48 de 103 = 46,60 por cento (peso 40); Testes 35 de 164 = 21,34 por cento (peso 20); geral 62,91 por cento. Depois, com {AUD-11} e {AUD-10} abertos neste documento e {TA-98} e {TA-99} abertos no Plano de Testes: 133 cards, 76 marcados; Construção 48 de 105 = 45,71 por cento, conta: 103 mais {AUD-11} mais {AUD-10} igual 105, marcados inalterados; Testes 35 de 166 = 21,08 por cento, conta: 164 mais {TA-98} mais {TA-99} igual 166. Geral = 0,40 x 100 + 0,40 x 45,71 + 0,20 x 21,08 = 40 + 18,29 + 4,22 = 62,51 por cento, arredondado para 63 por cento (antes, 62,91, arredondado para 63). O geral cai porque o universo cresceu dois cards em cada seção sem entrega nova, o que se declara em vez de se esconder.
Atualização de 2026-09-30, Inspeção: drifts do gate de {AUD-10}. Notas nos cards {AUD-10} (migração 0048, versão vigente derivada, endereço proibido no assunto, trilha legível, prévia para os dois papéis, ortografia, HTML de dois modelos e rastro da prova em produção), {AUD-12} (numera depois da 0048) e {F4-03} (a 0048 não é dele). Nenhum card novo, nenhuma marcação alterada.
Atualização de 2026-09-30, Inspeção: drifts do gate de {AUD-11}. Notas nos cards {AUD-11} (duas migrações, 0046 e 0047, e {F4-03} passa a 0048; leitor único por entidade; desativar refeição após revogar; onde vivem os textos; a restrição governa a escrita da meta), {F1-12} (a leitura de que a restrição só governa a leitura fica superada em 2026-09-30) e {F4-03} (migração 0048, categoria decidida, leitorUnico e inserção com visibilidade). Nenhum card novo, nenhuma marcação alterada.
Atualização de 2026-09-30, Inspeção: card {AUD-12}. Uma decisão de escopo da Inspeção entra como card novo da Onda AUD, autorado aberto: {AUD-12}, Textos de consentimento editáveis e versionados. Cumpre a ordem do dono de 15/09 de que toda regra de negócio seja configurável pelo painel de Super Administrador e estende RF-CORE-010 e RF-CORE-012 sem RF novo: os sete textos de consentimento passam a viver no mecanismo de texto versionado que existe desde {F1-18}, a concessão grava a versão exibida ao aluno, e a rota de consentimentos devolve restrito_a_especialidades como lista. Ordem da fila da Onda AUD: {AUD-11}, depois {AUD-10}, depois {AUD-12}, e só então {F4-03}; o card aparece nesta ordem no documento. Nenhuma marcação muda. Aritmética medida no publicado antes desta republicação (contagem de cartões no endpoint de estado): 133 cards, 77 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 49 de 105 = 46,67 por cento (peso 40); Testes 36 de 166 = 21,69 por cento (peso 20); geral 63,01 por cento. Depois, com {AUD-12} aberto neste documento e {TA-100} aberto no Plano de Testes: 134 cards, 77 marcados; Construção 49 de 106 = 46,23 por cento, conta: 105 mais {AUD-12} igual 106, marcados inalterados; Testes 36 de 167 = 21,56 por cento, conta: 166 mais {TA-100} igual 167. Geral = 0,40 x 100 + 0,40 x 46,23 + 0,20 x 21,56 = 40 + 18,49 + 4,31 = 62,80 por cento, arredondado para 63 por cento (antes, 63,01, também 63). O geral cai porque o universo cresceu um card em cada seção sem entrega nova, o que se declara em vez de se esconder.
Atualização de 2026-09-30, fechamento de {AUD-11}. Nutrição como sétima categoria de consentimento entregue e em produção (merge 6490072, https://fit.modulareasy.com, migrações 0046 e 0047, gates plan, db, qa (PASS, {TA-98} 61 de 61) e review (APPROVED) verdes): leitor único 2 de 10, consentimento por entidade 2 de 10, critério (h) 7 = 7, e profissional vinculado sem consentimento de nutrição deixa de ler refeição e meta (403 nomeado, nunca 200 vazio). Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 134 cards, 77 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 49 de 106 = 46,23 por cento, e depois do flip 50 de 106 = 47,17 por cento (peso 40), conta: 49 marcados mais {AUD-11} igual 50; Testes 36 de 167 = 21,56 por cento, e depois do flip 37 de 167 = 22,16 por cento (peso 20), conta: 36 marcados mais {TA-98} igual 37. Geral = 0,40 x 100 + 0,40 x 47,17 + 0,20 x 22,16 = 40 + 18,87 + 4,43 = 63,30 por cento, arredondado para 63 por cento (antes, 62,80, também 63). Próximo da fila: {AUD-10}, depois {AUD-12} e {F4-03}. Nenhum card novo nasce: continuam 134 cards no Roadmap e 167 no Plano de Testes.
Atualização de 2026-09-30, fechamento de {AUD-10}. Modelos de e-mail editáveis entregues e em produção (merge 17b62d6, migrações 0048 e 0049, https://fit.modulareasy.com, /api/estado na versão 17b62d6; gates plan, db, qa (PASS COM RESSALVAS, {TA-99} 12 de 12, rotas 38 de 38) e review (APPROVED) verdes): mfit.parametro de 44 para 33, texto de e-mail em código de 11 para 0, placar do verificador de parâmetros inalterado em 52 de 98, C 53, universo 151, perda declarada e ressalvas no cartão. Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 134 cards, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 50 de 106 = 47,17 por cento, e depois do flip 51 de 106 = 48,11 por cento (peso 40), conta: 50 marcados mais {AUD-10} igual 51; Testes 37 de 167 = 22,16 por cento, e depois do flip 38 de 167 = 22,75 por cento (peso 20), conta: 37 marcados mais {TA-99} igual 38. Geral = 0,40 x 100 + 0,40 x 48,11 + 0,20 x 22,75 = 40 + 19,24 + 4,55 = 63,79 por cento, arredondado para 64 por cento (antes, 63,30, arredondado para 63). Próximo da fila: {AUD-12}, depois {F4-03}. Nenhum card novo nasce: continuam 134 cards no Roadmap e 167 no Plano de Testes.
Atualização de 2026-09-30, fechamento de {AUD-12}. Textos de consentimento editáveis e versionados entregues (merge 4e5ed26, migrações 0050 e 0051; o qa provou em produção na versão 53c7fd2, mesmo código do merge; gates plan, db, qa (PASS COM RESSALVAS, {TA-100} 27 de 27) e review (APPROVED) verdes): texto de consentimento em código de 7 para 0, verifica-parametros em 51 de 98, C 54, universo 152, perda declarada e ressalvas no cartão. A Onda AUD não fecha com este card: continua com {F4-03} na fila. Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 134 cards, 79 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 51 de 106 = 48,11 por cento, e depois do flip 52 de 106 = 49,06 por cento (peso 40), conta: 51 marcados mais {AUD-12} igual 52; Testes 38 de 167 = 22,75 por cento, e depois do flip 39 de 167 = 23,35 por cento (peso 20), conta: 38 marcados mais {TA-100} igual 39. Geral = 0,40 x 100 + 0,40 x 49,06 + 0,20 x 23,35 = 40 + 19,62 + 4,67 = 64,29 por cento, arredondado para 64 por cento (antes, 63,79, também 64). Próximo da fila: {F4-03}. Nenhum card novo nasce: continuam 134 cards no Roadmap e 167 no Plano de Testes.
Atualização de 2026-09-30, fechamento de {F4-03}. Plano alimentar prescrito com refeições estruturadas entregue e em produção (merge 2f5c560, migração 0052, https://fit.modulareasy.com, /api/estado na versão 2f5c560; gates plan (APPROVED, releitura contra 4e5ed26), db (PASS), qa (PASS COM RESSALVAS, rodada 2 PASS) e review (APPROVED na rodada 2) verdes): tela /nutricao/planos nas duas pontas, plano criado pelo Nutricionista de vínculo ativo ou pelo aluno sem Nutricionista ativo, refeições na ordem de horário e calorias somadas pelo banco, edição só pelo autor e pelo Super Administrador. Placares medidos: fronteira de dado clínico 3 de 13, consentimento por entidade 3 de 10, leitor único 3 de 10, capacidade por especialidade 12 de 12, 5 de 5 tabelas de nutrição, verifica-parametros inalterado em 51 de 98, C 54, universo 152, cardinalidade 33 e listas 7. Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 134 cards, 80 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 52 de 106 = 49,06 por cento, e depois do flip 53 de 106 = 50,00 por cento (peso 40), conta: 52 marcados mais {F4-03} igual 53; Testes 39 de 167 = 23,35 por cento, e depois do flip 40 de 167 = 23,95 por cento (peso 20), conta: 39 marcados mais {TA-35} igual 40, com {TB-38} aberto por ser Beta. Geral = 0,40 x 100 + 0,40 x 50,00 + 0,20 x 23,95 = 40 + 20,00 + 4,79 = 64,79 por cento, arredondado para 65 por cento (antes, 64,29, arredondado para 64). Próximo da fila: {F4-04}. Nenhum card novo nasce: continuam 134 cards no Roadmap e 167 no Plano de Testes.
Atualização de 2026-09-30, Inspeção: drifts do gate de {F4-04}. Nota no card {F4-05} (a função mfit.comparacao_com_meta, migração 0053, como regra única da comparação do consumo com a meta, consumida por {F4-05}). Nenhum card novo e nenhuma marcação alterada: continuam 134 cards e 81 marcados, medidos na contagem de cards antes e depois.
Atualização de 2026-09-30, fechamento de {F4-04}. Painel diário de consumo contra a meta entregue e em produção (merge a6624e9, migração 0053, https://fit.modulareasy.com; gates plan (APPROVED COM TRAVA DE DRIFT), db (PASS), qa (PASS COM RESSALVAS, reconferência PASS) e review (APPROVED na rodada 3) verdes): quatro cartões e o total do dia em "Metas do dia", regra única da comparação em mfit.comparacao_com_meta, verificador da fronteira e de vínculo com chamada positiva obrigatória e PATCH concorrente de plano com 409. {TA-34} fecha 4 de 4 com a quarta cláusula deste card. Placares medidos: fronteira 3 de 13, consentimento por entidade 3 de 10, leitor único 3 de 10, capacidade por especialidade 12 de 12, verifica-parametros inalterado em 51 de 98, C 54, universo 152. Aritmética medida no publicado antes do flip (contagem de cartões no endpoint de estado): 134 cards, 81 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 53 de 106 = 50,00 por cento, e depois do flip 54 de 106 = 50,94 por cento (peso 40), conta: 53 marcados mais {F4-04} igual 54; Testes 40 de 167 = 23,95 por cento, e depois do flip 42 de 167 = 25,15 por cento (peso 20), conta: 40 marcados mais {TA-36} e {TA-34} igual 42, com {TB-39} e {TB-06} abertos por serem Beta. Geral = 0,40 x 100 + 0,40 x 50,94 + 0,20 x 25,15 = 40 + 20,38 + 5,03 = 65,41 por cento, arredondado para 65 por cento (antes, 64,79, também 65). Próximo da fila: {F4-05}. Nenhum card novo nasce: continuam 134 cards no Roadmap e 167 no Plano de Testes.
Atualização de 2026-09-30, Inspeção: drifts do gate de {F4-05}. Notas nos cards {F4-05} (a migração 0054, a função mfit.meta_atingida e o parâmetro nutricao_meta_tolerancia_pct, a entregar, e a regra do acompanhamento publicada em RF-DIET-005) e {F5-07} (o bloco de peso e composição do acompanhamento nasce lá). Nenhum card novo e nenhuma marcação alterada: continuam 134 cards e 82 marcados, medidos na contagem de cards do texto antes e depois.
Atualização de 2026-10-01, fechamento de {F4-05}. Acompanhamento nutricional pelo profissional entregue e em produção (merge f609a9c, migrações 0054 e 0055, https://fit.modulareasy.com; gates plan, db, qa e review verdes). A Fase 4 fecha em código: os cinco cards {F4-01} a {F4-05} estão entregues. Os Betas seguem [ ], porque dependem de operador real. Aritmética medida no publicado antes do flip (contagem de cartões do texto): 134 cards, 82 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 54 de 106 = 50,94 por cento, e depois do flip 55 de 106 = 51,89 por cento (peso 40), conta: 54 marcados mais {F4-05} igual 55; Testes 42 de 167 = 25,15 por cento, e depois do flip 43 de 167 = 25,75 por cento (peso 20), conta: 42 marcados mais {TA-37} igual 43. Geral = 0,40 x 100 + 0,40 x 51,89 + 0,20 x 25,75 = 40 + 20,75 + 5,15 = 65,90 por cento, arredondado para 66 por cento (antes, 65,41, arredondado para 65). Nenhum card novo nasce: continuam 134 cards no Roadmap e 167 no Plano de Testes. Próximo da fila: {F5-01}.
Atualização de 2026-10-01, Inspeção: drifts do gate de {F5-01}. Notas nos cards {F5-01} (migração 0056 e os dois tetos de texto da anamnese, a entregar; a ficha dos 46 campos; quem preenche qual seção; anamnese sem edição de conteúdo; o leitor único do módulo) e {F5-04}, {F5-05} e {F5-06} (a aba nasce dentro da casca de /saude). A decisão de produto está publicada no DRS (RF-SAUD-001, RN-009, L.2 e Apêndice de formulários). Nenhum card novo e nenhuma marcação alterada: continuam 134 cards e 83 marcados, medidos na contagem de cards do texto antes e depois.
Atualização de 2026-10-01, fechamento de {F5-01}. Anamnese datada entregue e em produção (merge 9cb16f3, repositório wladimirdjr/modularfit, migração 0056, https://fit.modulareasy.com (/api/estado na versão 9cb16f3); gates plan, db, qa e review verdes). A Fase 5 abre: {F5-01} entregue, {F5-02} a {F5-10} seguem abertos. Os Betas seguem [ ], porque dependem de operador real. Aritmética medida no publicado antes do flip (contagem de cartões do texto): 134 cards, 83 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 55 de 106 = 51,89 por cento, e depois do flip 56 de 106 = 52,83 por cento (peso 40), conta: 55 marcados mais {F5-01} igual 56; Testes 43 de 167 = 25,75 por cento, e depois do flip 44 de 167 = 26,35 por cento (peso 20), conta: 43 marcados mais {TA-38} igual 44. Geral = 0,40 x 100 + 0,40 x 52,83 + 0,20 x 26,35 = 40 + 21,13 + 5,27 = 66,40 por cento, arredondado para 66 por cento (antes, 65,90, também 66). Nenhum card novo nasce: continuam 134 cards no Roadmap e 167 no Plano de Testes. Próximo da fila: {F5-02}.
Atualização de 2026-10-06, fechamento de {F5-02}. Avaliação física em seis seções, com comparação entre avaliações, entregue e em produção (merge 3ab8395, repositório wladimirdjr/modularfit, migrações 0057 e 0058 aplicadas em produção em 06/10, https://fit.modulareasy.com (/api/estado na versão 87ae8e0, a mesma árvore do merge; o deploy da main em 3ab8395 segue em curso); gates plan, db, qa e review verdes: o qa reprovou a rodada 1 por um P1 e passou no reteste). A Fase 5 segue: {F5-01} e {F5-02} entregues, {F5-03} a {F5-10} seguem abertos. Os Betas seguem [ ], porque dependem de operador real. Aritmética medida no publicado antes do flip (contagem de cartões do texto): 134 cards, 84 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 56 de 106 = 52,83 por cento, e depois do flip 57 de 106 = 53,77 por cento (peso 40), conta: 56 marcados mais {F5-02} igual 57; Testes 44 de 167 = 26,35 por cento, e depois do flip 45 de 167 = 26,95 por cento (peso 20), conta: 44 marcados mais {TA-39} igual 45. Geral = 0,40 x 100 + 0,40 x 53,77 + 0,20 x 26,95 = 40 + 21,51 + 5,39 = 66,90 por cento, arredondado para 67 por cento (antes, 66,40, que arredondava para 66). Nenhum card novo nasce: continuam 134 cards no Roadmap e 167 no Plano de Testes. A migração 0058 (CHECK estrito de secoes da anamnese) foi aprovada pelo Fiscal sem card próprio e entra no fechamento de {F5-02}. Próximo da fila: {F5-03}.
Atualização de 2026-10-06, Inspeção: drifts do gate de {F5-03} e card {AUD-13}. Notas publicadas antes do código no card {F5-03} (Drifts 1 a 6 e o P2 visual), nas Notas de RF-SAUD-002, RF-SAUD-003 e RN-004 do DRS e no cartão {ART-FORM-05} do Inventário, sem marcar entrega: o cálculo aparece no detalhe e na comparação, com a lista de fora; as constantes de literatura entram no verificador como exceção C nomeada; as zonas ficam fora da comparação; o cálculo sem insumo diz "não calculado" e nunca zero; o arredondamento é meio para cima; e o corpo inválido responde 400 corpo_invalido. Um card novo da Onda AUD entra autorado aberto: {AUD-13}, Corpo de requisição inválido responde 400 em todas as rotas, com a ordem declarada "depois de {F5-03}, antes de {F5-04}"; no Plano de Testes entra {TA-101}, aberto. Nenhuma marcação muda. Aritmética medida no publicado antes desta republicação (contagem de cartões do texto): 134 cards, 85 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 57 de 106 = 53,77 por cento (peso 40); Testes 45 de 167 = 26,95 por cento (peso 20); geral 66,90 por cento. Depois, com {AUD-13} aberto neste documento e {TA-101} aberto no Plano de Testes: 135 cards, 85 marcados; Construção 57 de 107 = 53,27 por cento, conta: 106 mais {AUD-13} igual 107, marcados inalterados; Testes 45 de 168 = 26,79 por cento, conta: 167 mais {TA-101} igual 168. Geral = 0,40 x 100 + 0,40 x 53,27 + 0,20 x 26,79 = 40 + 21,31 + 5,36 = 66,67 por cento (antes, 66,90). O geral cai porque o universo cresceu um card em cada seção sem entrega nova, o que se declara em vez de se esconder.
Atualização de 2026-10-06, fechamento de {F5-03}. Cálculos derivados da avaliação entregues e em produção (merge 08b35ed, repositório wladimirdjr/modularfit, sem migração, https://fit.modulareasy.com (a mesma árvore já provada em produção como 208a883; o deploy da main em 08b35ed segue em curso); gates plan, review e qa verdes): índice de massa corporal, relação cintura quadril, frequência cardíaca máxima e as cinco zonas, calculados na leitura com a fórmula visível, nenhum gravado; H1 provado (24,7, 0,90, 190 e as zonas 95 a 114 até 171 a 190); 400 corpo_invalido em avaliações e anamneses (o restante das rotas é {AUD-13}); e subgrid nas listas clínicas com degrau 0 em 6 larguras. {AUD-13} segue aberto. Os Betas seguem [ ], porque dependem de operador real. Aritmética medida no publicado antes do flip (contagem de cartões do texto): 135 cards, 85 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 57 de 107 = 53,27 por cento, e depois do flip 58 de 107 = 54,21 por cento (peso 40), conta: 57 marcados mais {F5-03} igual 58; Testes 45 de 168 = 26,79 por cento, e depois do flip 46 de 168 = 27,38 por cento (peso 20), conta: 45 marcados mais {TA-40} igual 46. Geral = 0,40 x 100 + 0,40 x 54,21 + 0,20 x 27,38 = 40 + 21,68 + 5,48 = 67,16 por cento, arredondado para 67 por cento (antes, 66,67). Nenhum card novo nasce: continuam 135 cards no Roadmap e 168 no Plano de Testes. Próximo da fila: {AUD-13}, depois {F5-04}.
Atualização de 2026-10-06, fechamento de {AUD-13}. Corpo de requisição inválido responde 400 em todas as rotas, entregue e em produção (merge 1c32c5e, repositório wladimirdjr/modularfit, sem migração, https://fit.modulareasy.com (a mesma árvore já provada em produção como ecef515; o deploy da main em 1c32c5e segue em curso); gates plan, review e qa verdes): ponto único server/src/http/corpoJson.ts, placar 52 = 37 JSON + 1 multipart + 14 sem corpo, tabela 38 = 38 no ponto único, 0 leituras fora, verificador verificar:corpo com 13 decoys; H1 provado em produção ({TA-101}); perdas ratificadas no card. Os Betas seguem [ ], porque dependem de operador real. Aritmética medida no publicado antes do flip (contagem de cartões do texto): 135 cards, 86 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 58 de 107 = 54,21 por cento, e depois do flip 59 de 107 = 55,14 por cento (peso 40), conta: 58 marcados mais {AUD-13} igual 59; Testes 46 de 168 = 27,38 por cento, e depois do flip 47 de 168 = 27,98 por cento (peso 20), conta: 46 marcados mais {TA-101} igual 47. Geral = 0,40 x 100 + 0,40 x 55,14 + 0,20 x 27,98 = 40 + 22,056 + 5,595 = 67,65 por cento, arredondado para 68 por cento (antes, 67,16, que arredondava para 67). Nenhum card novo nasce: continuam 135 cards no Roadmap e 168 no Plano de Testes. Próximo da fila: {F5-04}.
Atualização de 2026-10-06, Inspeção: drifts do gate de {F5-04}. Notas publicadas antes do código no card {F5-04} (Drifts 1 a 11 e a migração 0059), no card {F5-09} (Drift 1) e no card {F6-05} (Drift 5); nas Notas de RF-SAUD-004 e de RN-009, no Apêndice de formulários e em L.2 do DRS; nos cartões {ART-ENT-06}, {ART-FORM-07}, {ART-INT-02}, {ART-ACS-08}, {ART-ACS-10} e {ART-CFG-01} do Inventário; e, no Plano de Testes, em {TA-41} (os casos de prova) e nas Notas de placar parcial de {TA-07}, {TA-10} e {TA-85}, sem marcar entrega. Nenhum card novo e nenhuma marcação alterada: continuam 135 cards e 87 marcados, medidos na contagem de cards do texto antes e depois.
Atualização de 2026-10-06, fechamento de {F5-04}. Exames com anexo em repositório privado entregues e em produção (merge b078e76, repositório wladimirdjr/modularfit, migração 0059 aplicada em produção em 06/10, limite de envio de 51 megabytes só no bloco do servidor do produto, https://fit.modulareasy.com (a mesma árvore já provada em produção como bde96b3; o deploy da main em b078e76 segue em curso); gates plan, db, review e qa verdes): tabela mfit.exame, ramo anexos_megabytes na cota, três tetos de texto como parâmetros (parâmetros de 38 para 41), lista tipo_exame (listas de 8 para 9), escrita única de arquivos e aba Exames na casca de /saude; H1 provado em produção ({TA-41}: 20 MiB exatos abrem por endereço assinado, 20 MiB mais um byte e extensão fora da lista são recusados com a mensagem do campo). {F5-09}, {F6-05}, {F5-10}, {F5-08} e {F8-09} seguem abertos. Os Betas seguem [ ], porque dependem de operador real. Aritmética medida no publicado antes do flip (contagem de cartões do texto): 135 cards, 87 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 59 de 107 = 55,14 por cento, e depois do flip 60 de 107 = 56,07 por cento (peso 40), conta: 59 marcados mais {F5-04} igual 60; Testes 47 de 168 = 27,98 por cento, e depois do flip 48 de 168 = 28,57 por cento (peso 20), conta: 47 marcados mais {TA-41} igual 48. Geral = 0,40 x 100 + 0,40 x 56,07 + 0,20 x 28,57 = 40 + 22,43 + 5,71 = 68,14 por cento, arredondado para 68 por cento (antes, 67,65, que arredondava para 68). Nenhum card novo nasce: continuam 135 cards no Roadmap e 168 no Plano de Testes. Próximo da fila: {F5-05}.
Atualização de 2026-10-06, Inspeção: drifts do gate de {F5-05}. Notas publicadas antes do código no card {F5-05} (Drifts 1 a 10 e as migrações 0060 e 0061), nos cards {F5-10} (Drift 2), {F6-05} (Drift 3) e {F7-09} (Drift 7); nas Notas de RF-SAUD-005, de RN-009 e de L.2, em E.1 e no Apêndice de formulários do DRS; nos cartões {ART-FORM-08} e {ART-CFG-01} do Inventário; e, no Plano de Testes, nas Notas de placar parcial de {TA-07}, {TA-10} e {TA-85}, sem marcar entrega. Nenhum card novo e nenhuma marcação alterada: continuam 135 cards e 88 marcados, medidos na contagem de cards do texto antes e depois.
Atualização de 2026-10-06, fechamento de {F5-05}. Prescrições com aviso de não substituição do receituário entregues e em produção (merge de718a6, repositório wladimirdjr/modularfit, migrações 0060 e 0061 aplicadas em produção em 07/10 (UTC; noite de 06/10 em Brasília), https://fit.modulareasy.com (a mesma árvore já provada em produção como 9510270; o deploy da main em de718a6 segue em curso); gates plan, db, review e qa verdes): tabela mfit.prescricao só com escrita do Médico vinculado, duração estruturada com vigência calculada na leitura (o dia da prescrição é o primeiro dia; "hoje" é a data de America/Sao_Paulo, num ponto único), aviso do receituário versão 1 editável em /admin/textos, parâmetros de 41 para 42, listas fechadas 9; H1 provado em produção ({TA-42}). Correções extras no mesmo merge: datas exibidas em Brasília em um ponto único do web, em todas as telas, e nascimento do perfil como data pura (bug anterior ao card, achado pelo qa). As duas regras da vigência ficam gravadas como Nota no card {F7-09}. {F5-06}, {F5-07}, {F5-08}, {F5-09}, {F5-10} e {F6-05} seguem abertos; {ART-FORM-08} não se marca (fecha em {F5-06}). Os Betas seguem [ ], porque dependem de operador real. Aritmética medida no publicado antes do flip (contagem de cartões do texto): 135 cards, 88 marcados, Planejamento 28 de 28 = 100 por cento (peso 40); Construção 60 de 107 = 56,07 por cento, e depois do flip 61 de 107 = 57,01 por cento (peso 40), conta: 60 marcados mais {F5-05} igual 61; Testes 48 de 168 = 28,57 por cento, e depois do flip 49 de 168 = 29,17 por cento (peso 20), conta: 48 marcados mais {TA-42} igual 49. Geral = 0,40 x 100 + 0,40 x 57,01 + 0,20 x 29,17 = 40 + 22,80 + 5,83 = 68,64 por cento, arredondado para 69 por cento (antes, 68,14, que arredondava para 68). Nenhum card novo nasce: continuam 135 cards no Roadmap e 168 no Plano de Testes. Próximo da fila: {F5-06}.
Atualização de 2026-10-06, Inspeção: drifts do gate de {F5-06}. Notas publicadas antes do código no card {F5-06} (Drifts 1 a 9 e a migração 0062), nos cards {F5-10} (Drift 4), {F6-05} (Drift 4) e {F7-10} (Drifts 2 e 3); nas Notas de RF-SAUD-006, de RN-009 e de L.2, em E.1 e no Apêndice de formulários do DRS; nos cartões {ART-ENT-06}, {ART-FORM-08} e {ART-CFG-01} do Inventário; e, no Plano de Testes, em {TA-43} (os casos de prova) e nas Notas de placar parcial de {TA-07}, {TA-10} e {TA-85}, sem marcar entrega. Nenhum card novo e nenhuma marcação alterada: continuam 135 cards e 89 marcados, medidos na contagem de cards do texto antes e depois.
Planejamento
Concluída na aprovação do DRS. Vale 40 por cento do projeto (seção Planejamento). Estas etapas cobrem tudo que aconteceu antes de codar.
0.1 Entendimento e Elicitação
0.2 Especificação (o DRS)
0.3 Documentos companheiros
0.4 Validação e Qualidade
0.5 Publicação e Aprovação
Fase M, Mockup Navegável (pós-Planejamento, gate de aprovação)
Dependência: DRS aprovado (Planejamento fechado). A construção (fases seguintes) NÃO começa com esta fase aberta.
DRS, bloco J.5 Artefatos de apoio e Apêndice de identidade visual. Todas as telas do catálogo de telas, nas variações de papel (aluno no celular, quatro painéis de profissional, administração), com marca e cores oficiais, base de estilo compartilhada, motor configurado por página e navegador flutuante Mapa do mockup.
Como verificar (teste primeiro): todas as páginas respondem com sucesso na URL publicada; cada papel exibe o próprio painel; o fluxo de execução de sessão e o de registro de exame demonstram o passo a passo real em script; inspeção visual página a página em 375 e 1440; proporção de toda imagem conferida pela medição, não a olho.
Aceite (H1): Dado o mockup publicado, Quando o dono do projeto navega pelo Mapa do mockup, Então alcança toda tela do catálogo de telas do DRS, E cada papel aparece com o painel e a identidade corretos.
Gate. Apresentar ao dono do projeto, aplicar ajustes no próprio mockup e obter a aprovação.
Como verificar (teste primeiro): aprovação registrada; DRS atualizado no bloco J.5 declarando o mockup referência visual aprovada e guia do desenvolvimento, com o endereço publicado.
Aceite (H1): Dado o mockup aprovado, Quando a construção de uma tela começa, Então a página correspondente do mockup é o guia, E divergência visual se resolve consultando o mockup.
Definição de Pronto da Fase M: mockup publicado, navegável de ponta a ponta, aprovado e vinculado no DRS.
Fase 1, Fundação, identidade e acesso
DRS G.1 e ADR-009. Instância própria, sem compartilhar banco nem identidade com outro produto; repositório público de mídia e repositório privado de dado sensível, com políticas distintas.
O que implementar: repositório de código próprio, compose isolado com banco e aplicação, migração de fundação (esquema, papel de execução sem privilégio de contorno de política, tabela de migrações), rota de estado que verifica o banco, os dois repositórios de arquivo no volume próprio, cópia de segurança diária e semanal, e o endereço público com proxy reverso.
Como verificar (teste primeiro): um arquivo no repositório público abre por endereço direto e um arquivo no privado retorna acesso negado por endereço direto.
Aceite (H1): Dado os dois repositórios provisionados, Quando o endereço direto de cada um é acessado sem sessão, Então o público responde conteúdo e o privado responde acesso negado.
DRS Apêndice de identidade visual e F.1. Tokens de cor, tipografia, espaçamento e raio numa fonte única; tamanho sempre relativo à base do contêiner; tema escolhido pelo usuário e lembrado.
Como verificar (teste primeiro): alterar a base do contêiner reescala toda a tipografia proporcionalmente; nenhum componente declara tamanho em pixel fixo fora de borda e sombra.
Aceite (H1): Dado o sistema de estilo aplicado, Quando o tema é alternado, Então toda tela troca de superfície e texto sem elemento ilegível, E o tema escolhido persiste após recarregar.
Nota [F1-17]: /estilo é referência visual de desenvolvimento, alcançável só por endereço direto, fora de menu e fora de link de qualquer página; não é tela de produto e por isso não entra no catálogo de telas do DRS nem conta em nenhuma ficha {ART-SCR-0x}. O mesmo vale para /api/estado, que é verificação de saúde. Nenhum card futuro deve reconectar o link a partir da home.
RF-CORE-001.
O que implementar: tela de entrada, emissão de sessão em cookie restrito ao servidor, renovação, bloqueio após seis tentativas em quinze minutos.
Como verificar (teste primeiro): credencial correta entra, credencial errada retorna erro genérico, sexta falha bloqueia por trinta minutos.
Aceite (H1): Dado um aluno com conta ativa e senha correta, Quando envia e-mail e senha na tela de entrada, Então recebe uma sessão válida e é levado ao painel do aluno, E uma credencial errada retorna erro de autenticação sem revelar se o e-mail existe.
RF-CORE-002.
Como verificar (teste primeiro): conta criada com plano gratuito e todos os campos opcionais nulos; nenhuma tela bloqueia por campo opcional.
Aceite (H1): Dado um visitante na tela de cadastro, Quando envia nome, e-mail válido e senha com no mínimo oito caracteres, Então a conta é criada com papel de aluno, plano gratuito e status ativo, E o aluno alcança o painel com todos os campos opcionais do perfil vazios.
RF-CORE-003.
Como verificar (teste primeiro): as quatro máscaras aceitam o formato próprio e recusam os outros três.
Aceite (H1): Dado um visitante que escolhe a especialidade Personal Trainer, Quando informa o registro 123456-G/AM, Então a conta é criada com especialidade e registro gravados, E o mesmo número no formato de outra especialidade é recusado com a mensagem do campo indicando o formato esperado.
Nota [F1-17]: os quatro rótulos de CATALOGO_ESPECIALIDADES ganharam um segundo leitor, e ele é público, a partir de {F1-17}: renomear um rótulo muda a página pública inicial. O décimo segundo verificador (ops/verifica-home.mjs) reprova se os dois lados divergirem, e o conserto é sempre no espelho (a fonte de texto da home), nunca uma terceira lista.
RF-CORE-004.
Aceite (H1): Dado um usuário autenticado na tela de perfil, Quando altera um campo e salva, Então o valor novo aparece na tela após recarregar, E a alteração fica registrada na trilha de auditoria com autor, campo, valor anterior e valor novo.
RF-CORE-005.
O que implementar: também passa a enviar a confirmação de conta (ART-MSG-01) das contas criadas em F1-04 e F1-05, incluindo as já existentes.
Aceite (H1): Dado um usuário que solicita redefinição para um e-mail cadastrado, Quando abre o endereço recebido dentro de sessenta minutos e define uma senha nova, Então consegue entrar com a senha nova, E o mesmo endereço usado uma segunda vez retorna erro de link inválido.
RF-CORE-006, D.2 e ADR-003.
O que implementar: o auxiliar único de decisão de acesso mfit.pode_ler_aluno, com as quatro condições de leitura de D.2 e a classe restrita como parâmetro; a tabela vinculo, da qual o auxiliar depende para decidir; a política nas entidades de dado de aluno que já existem hoje (aceite_termos); o gabarito que todo card futuro aplica ao criar a tabela dele; e o verificador que reprova a criação de tabela de dado de aluno sem política.
Como verificar (teste primeiro): os trinta e seis casos nascem aqui como arnês parametrizado por entidade, executado antes da interface existir; as duplas das entidades que ainda não existem são reportadas como pendentes com o nome do card dono, e passam a verde no card que cria cada tabela; o verificador ops/verifica-acesso-aluno.mjs roda dentro do node --test e reprova qualquer tabela de dado de aluno sem política.
Aceite (H1): Dado o profissional P2 sem vínculo com o aluno A1, Quando solicita por identificador direto o registro de A1 na entidade aceite_termos ou em qualquer entidade de dado de aluno já existente, Então a resposta é negada, E o profissional P1 vinculado a A1 obtém o registro com sucesso nas entidades já existentes, E a entidade restrita nega tanto P1 quanto P2, E a criação de uma tabela de dado de aluno sem política reprova o verificador. O H1 de RF-CORE-006 no DRS não muda, continua exigindo os trinta e seis casos, que fecham em TA-06 na Fase 8.
RF-CORE-007.
O que implementar: a coluna aluno_id na trilha de auditoria; o leitor único de registro sensível no servidor (server/src/acesso/leituraSensivel.ts), que é o único caminho pelo qual qualquer rota lê uma das seis entidades sensíveis e que grava a entrada acesso_administrativo na MESMA transação da leitura; a marcação sensivel na lista única de entidades; a seção nova do gabarito (GABARITO-ACESSO-ALUNO.md), que todo card da Fase 5 aplica; e o verificador (ops/verifica-leitura-sensivel.mjs) que reprova rota que leia entidade sensível por fora do leitor único.
Como verificar (teste primeiro): os seis casos de TA-07 nascem aqui como arnês parametrizado por entidade, executado antes de a interface existir; as seis entidades ainda não existem e são reportadas como pendentes com o nome do card dono; o mecanismo é provado hoje contra uma tabela sensível de ensaio criada e desfeita dentro da transação do próprio teste.
Aceite (H1): Dada uma entidade marcada como sensível, Quando o Administrador a lê pelo leitor único, Então o conteúdo é servido e existe exatamente uma entrada acesso_administrativo com autor, aluno, entidade, identificador e momento, E quando o titular lê a própria linha não existe entrada nenhuma, E uma rota que leia a entidade sensível por fora do leitor único reprova o verificador. O H1 de RF-CORE-007 no DRS não muda: ele continua exigindo as seis categorias e a tela, e é TA-07 que o fecha.
RF-CORE-008 e RN-008.
O que implementar: a política de DELETE explícita (FOR DELETE TO mfit_app USING (papel_atual() = 'administrador')) em vinculo, única das onze entidades que existe hoje; o gatilho genérico de auditoria de remoção, emitindo excluir em AFTER DELETE e desativar quando a coluna de desativação muda; a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md), que todo card dono aplica ao criar a tabela dele; o campo removível na lista única de entidades com a trava aritmética das onze; e o verificador que reprova, por critério, toda tabela de dado de aluno sem política de DELETE, ou com política de DELETE que não exija o papel administrador, ou sem o gatilho de remoção.
Como verificar (teste primeiro): os casos nascem como arnês parametrizado por entidade, executado antes de a interface existir; as dez entidades ainda ausentes são reportadas como pendentes com o nome do card dono, placar 1 servida mais 10 pendentes igual a 11; o mecanismo é provado hoje contra vinculo e contra uma tabela de ensaio criada e desfeita dentro da transação do próprio teste, incluindo a prova negativa de uma segunda tabela de ensaio sem política de DELETE, que o verificador tem que reprovar.
Aceite (H1): Dada uma entidade de dado de aluno marcada como removível, Quando um aluno ou um profissional pede a exclusão definitiva, Então nenhuma linha é apagada e a rota responde negação explícita, E quando ele pede a desativação a linha passa ao estado inativo e continua legível no histórico com entrada de auditoria desativar, E quando o Administrador pede a exclusão definitiva a linha é apagada e existe entrada excluir com autor, aluno, entidade, identificador e momento, E uma tabela de dado de aluno sem política de DELETE reprova o verificador.
Nota: o H1 de RF-CORE-008 no DRS não muda, continua exigindo as onze entidades, e é TA-08 que o fecha quando todas existirem e as listas de trabalho delas estiverem no ar.
RF-CORE-009 e D.5.
O que implementar: REVOKE UPDATE, DELETE ON mfit.auditoria FROM mfit_app e o mesmo em mfit.aceite_termos; RLS de leitura em mfit.auditoria restrita ao Administrador, mantendo a inserção livre para o leitor único de {F1-09} e para os gatilhos; o índice de listagem por momento; a rota GET /api/admin/auditoria com filtros e paginação, privativa do Administrador; a tela /admin/auditoria fiel à página 36 do mockup; o trilho de navegação do Administrador, contendo só o que existe; e o verificador que reprova, por catálogo, toda tabela declarada apenas acrescida que ainda tenha UPDATE ou DELETE para mfit_app.
Como verificar (teste primeiro): os casos de banco rodam contra o Postgres efêmero antes de a tela existir, e a negação é provada pelo código de erro do Postgres (42501), nunca pelo texto da mensagem.
Aceite (H1): Dado um usuário que altera um campo do próprio perfil, Quando salva, Então existe a entrada de auditoria com autor, entidade, identificador, campo, valor anterior, valor novo e momento, E a tela /admin/auditoria a exibe e a encontra por autor, entidade e período, E toda tentativa de editar ou remover qualquer entrada da trilha, feita pela aplicação em qualquer um dos três papéis inclusive o Administrador, é recusada pelo banco com erro de permissão, E o mesmo vale para aceite_termos, E uma tabela declarada apenas acrescida que tenha UPDATE ou DELETE para o papel da aplicação reprova o verificador.
Nota: o H1 de RF-CORE-009 no DRS não muda, ele continua falando da ficha, e é TA-09 que o fecha, na Fase 3.
RF-CORE-010, RN-001 e ADR-010.
O que implementar: a entidade mfit.consentimento com a unicidade parcial da linha ativa; os dois auxiliares SECURITY DEFINER (mfit.tem_consentimento para a escrita, mfit.consentimento_visivel para a leitura pelo profissional); a quinta seção do gabarito, que toda tabela sensível aplica na própria migração de criação; o campo categoriaConsentimento na lista única, com a trava de bijeção contra o enum do banco; o verificador por catálogo que reprova toda tabela marcada sensível cuja política de escrita não exija o consentimento da categoria dela; as rotas GET/POST/DELETE /api/consentimentos; a tela /privacidade fiel à página 20 do mockup, sem o botão de exportar; e o cartão de consentimento como componente reutilizável, que as páginas 13 e 14 vão consumir quando existirem.
Como verificar (teste primeiro): os casos de banco rodam contra o Postgres efêmero antes de a tela existir, contra uma tabela de ensaio criada e desfeita dentro da transação do próprio teste (a mesma técnica de {F1-10} e {F1-11}), e a negação é provada pelo código de erro do Postgres (42501), nunca pelo texto da mensagem.
Aceite (H1): Dado um aluno que nunca consentiu uma categoria, Quando ele ou um profissional vinculado tenta escrever um registro daquela categoria, Então o banco recusa a escrita com erro de permissão, E a tela /privacidade apresenta a categoria como não concedida com a ação Conceder, E após conceder pela rota real a mesma escrita conclui, E após revogar ela volta a ser recusada enquanto os registros já existentes continuam legíveis ao titular e ao Administrador e deixam de ser legíveis ao profissional vinculado, E a concessão e a revogação geram entrada de auditoria, E uma tabela marcada sensível cuja política de escrita não exija o consentimento da categoria dela reprova o verificador.
Nota: o H1 de RF-CORE-010 no DRS não muda, ele continua falando do exame, e é TA-10 que o fecha, quando as seis existirem.
Nota (Inspeção, {AUD-11}, drift 7, 2026-09-30): a leitura registrada no histórico de mudanças deste card, de que a restrição por especialidade governa só a leitura, fica superada em 2026-09-30 para a escrita de terceiro que depende de ler a linha anterior: a restrição escolhida pelo aluno governa também a escrita, e o profissional fora dela não grava (RN-003, RF-CORE-010). O que valeu até esta data continua descrito, sem apagar. Detalhe em {AUD-11}.
RF-PLAN-001 e ADR-012.
O que implementar: as seis colunas tipadas de limite no lugar do documento estruturado, o enum de recorrência, a coluna padrao que desamarra o cadastro do nome do plano, o valor de referência anulável para as duas faixas ainda sem preço, a semeadura de Essencial e Completo com os limites do Apêndice, a RLS da tabela (leitura livre à aplicação, escrita privativa do Administrador, DELETE revogado), a auditoria de criação e de alteração reusando as funções genéricas que já existem, o auxiliar único mfit.limite_do_aluno e o auxiliar de comparação mfit.dentro_do_limite que {F1-15} vai consumir, a lista única DIMENSOES_PLANO com trava de bijeção contra as colunas do catálogo, o verificador que reprova nome de plano literal em código e dimensão sem coluna, as rotas GET/POST/PATCH /api/admin/planos mais GET /api/planos, e a tela /admin/planos fiel à página 34 do mockup, sem o botão de atribuir.
Como verificar (teste primeiro): os casos de banco rodam contra o Postgres efêmero antes de a tela existir; a negação de escrita é provada pelo código de erro do Postgres (42501), nunca pelo texto da mensagem; e a cláusula do H1 sobre "a verificação seguinte" é provada contra a única dimensão cujo dado real existe hoje (vinculos_ativos, contada em mfit.vinculo, entregue em {F1-08}), na mesma conexão, sem reinício e sem nova publicação.
Aceite (H1): Dado o Administrador autenticado em /admin/planos, Quando cria um plano informando nome, valor, recorrência e as seis dimensões de limite, Então o plano passa a constar na lista de planos ativos servida para atribuição (GET /api/planos), E ao alterar um limite desse plano pela tela, a consulta seguinte de mfit.dentro_do_limite para um aluno daquele plano já responde pelo valor novo, na mesma conexão, sem reinício do serviço e sem nova publicação do sistema, E a criação e a alteração geram entrada de auditoria visível em /admin/auditoria, E um plano com aluno vinculado não pode ser apagado, apenas descontinuado, E renomear o plano padrão não quebra o cadastro de aluno novo.
Nota: o H1 de RF-PLAN-001 no DRS não muda, ele continua falando da lista disponível para atribuição, e é TA-16 que o fecha, quando {F1-14} e {F1-15} existirem.
Fechamento [2026-09-07]: entregue e em produção (bae8f0d, migração 0013_catalogo_planos.sql). Ficou pronto: seis colunas limite_* tipadas no lugar do documento jsonb, coluna padrao por índice único parcial, tela /admin/planos, verificador verificar:planos, RLS e auditoria da tabela. QA achou e corrigiu BUG-03 (etiqueta PADRÃO não atualizava sem reload). TA-16 continua pendente de {F1-14} e {F1-15}.
RF-PLAN-002.
O que implementar: o auxiliar único mfit.autor_com_acesso_ativo(); a conjunção dele no WITH CHECK (nunca no USING) das cinco políticas de escrita já existentes alcançáveis pelo aluno; a seção nova do gabarito, que toda tabela de dado de aluno aplica ao nascer; a negação na camada de API dentro de sessaoAtual, valendo para todo método que não seja de leitura e para toda rota autenticada, inclusive as que ainda não existem; o código de erro nomeado acesso_inativo, distinto dos três que a entrada já emite; as rotas GET /api/admin/alunos?q= e PATCH /api/admin/alunos/:id/plano; o verificador que reprova tabela de dado de aluno com política de escrita sem a conjunção; o painel "Atribuir plano a aluno" em /admin/planos; e a humanização do status mais o aviso de conta inativa no painel do Aluno e em /perfil.
Como verificar (teste primeiro): os casos de banco rodam contra o Postgres efêmero antes de a tela existir; a negação de escrita é provada pelo código de erro do Postgres (42501), nunca pelo texto da mensagem; e a negação na API é provada pelas quatro rotas de escrita que existem hoje, uma a uma, mais o decoy de uma rota de ensaio criada dentro do próprio teste, que prova que a cobertura é do ponto único e não da lista.
Aceite (H1): Dado um aluno no plano gratuito, Quando o Administrador o busca em /admin/planos e lhe atribui um plano pago mantendo o status ativo, Então a próxima requisição do aluno já lê o plano novo do banco, sem reinício, sem nova sessão e sem nova publicação, E a atribuição gera entrada de auditoria com autor, campo, valor anterior e valor novo, visível em /admin/auditoria, E ao receber o status inativo o mesmo aluno continua entrando no sistema e continua lendo o próprio perfil, o próprio plano e o próprio histórico, E toda escrita dele é recusada, pela API com código próprio e pelo banco com erro de permissão, E nenhum aluno ativo, nem profissional, nem o Administrador tem qualquer escrita afetada.
Fechamento [2026-09-08]: entregue e em produção (37ad0ee, migração 0014_acesso_do_aluno.sql). Ficou pronto: mfit.autor_com_acesso_ativo() no WITH CHECK das cinco políticas de escrita do aluno, negação na camada de API por sessaoAtual com erro acesso_inativo, rotas de busca e atribuição, painel em /admin/planos, aviso de acesso inativo no painel, /perfil e /privacidade. RF-PLAN-002 fecha na parte da atribuição e do status inativo; a parte de limite continua em {F1-15}. TA-16 sobe para 2 de 3, TA-17 fecha 1 de 2, nenhum marcado.
RF-PLAN-003.
O que implementar: o auxiliar mfit.dentro_do_limite_do_aluno(p_aluno_id, p_dimensao, p_ignorar_id), que conta por dentro, com SECURITY DEFINER, e devolve true antes de contar quando a dimensão é ilimitada; a conjunção dele no WITH CHECK das duas políticas de escrita de mfit.vinculo (vinculo_escrita e vinculo_atualizacao), guardada por estado = 'ativo' e excluindo a própria linha por id; a função mfit.plano_que_amplia; o tradutor único server/src/planos/negacaoDeEscrita.ts, que transforma o erro de permissão do banco em resposta nomeada e responde acesso antes de limite; o código de erro limite_do_plano (409), distinto dos que o produto já emite; a seção do gabarito reescrita com o medido; os três campos novos de DIMENSOES_PLANO (forma, tabelaDona, cardDono) com trava própria; e o décimo verificador, com placar próprio.
Como verificar (teste primeiro): os casos rodam contra o Postgres efêmero antes de qualquer rota; a recusa é provada pelo código de erro de permissão do Postgres, nunca pelo texto; o caminho do H1 é provado pelo UPDATE de pendente para ativo em mfit.vinculo, que é o que o aceite de {F2-06} vai fazer, e não só pelo INSERT; e o ilimitado é provado verificando que a contagem não rodou, não só pela ausência de recusa.
Aceite (H1): Dado um aluno no plano Gratuito, cujo limite de vínculos ativos é um, e já com um vínculo ativo, Quando a entrada de um segundo vínculo em estado ativo é tentada, pelo próprio aluno, pelo profissional ou pelo Administrador, Então a operação é recusada pela camada de dados com erro de permissão, e a API a traduz em resposta que nomeia a dimensão, o limite, o plano atual e o plano que o amplia, E após a administração atribuir o plano Essencial a mesma operação conclui na próxima requisição, sem nova sessão, sem reinício e sem nova publicação, E no plano Completo, cuja dimensão é ilimitada, a operação conclui em qualquer quantidade, sem recusa e sem que contagem alguma seja executada, E encerrar, recusar ou cancelar vínculo nunca é recusado por limite, mesmo com o aluno acima do teto, E nenhuma escrita de profissional, de Administrador ou de aluno abaixo do limite é afetada.
Nota: a conjunção de limite (mfit.dentro_do_limite_do_aluno) e a de acesso (mfit.autor_com_acesso_ativo, {F1-14}) são perguntas diferentes e ficam separadas, nunca fundidas num auxiliar só; o sujeito da primeira é o titular da linha, o da segunda é o ator da sessão. Dentro do WITH CHECK a ordem de avaliação do AND não é observável nem garantida, as duas conjunções falham com o mesmo erro de permissão; a ordem "acesso antes de limite" existe em dois lugares reais: em middleware/sessaoAtual.ts, que recusa toda escrita do aluno inativo com acesso_inativo antes de o tratador rodar, e no tradutor negacaoDeEscrita.ts, que pergunta acesso primeiro e só depois limite, para a escrita que porventura não passe pelo middleware.
RF-PLAN-004.
O que implementar: a função mfit.consumo_do_aluno(p_aluno_id, p_dimensao, p_ignorar_id), que recebe o CASE inteiro hoje preso dentro de mfit.dentro_do_limite_do_aluno, com a mesma mensagem de exceção; o CREATE OR REPLACE de dentro_do_limite_do_aluno, que passa a chamá-la, mantendo o retorno antecipado do ilimitado antes da chamada; mfit.plano_vigente_desde, definer, que devolve só um instante; a rota GET /api/meu-plano, privativa do aluno, que lê pelo sub da sessão e nunca por id do pedido; a tela /plano da página 17 com o cartão do plano e as seis linhas de consumo; o link "Plano" na fileira de links do painel do Aluno; as classes .mf-consumo* e o modificador .mf-aviso--bloco; e o décimo primeiro verificador, com placar próprio.
Como verificar (teste primeiro): os casos rodam contra o Postgres efêmero antes de a tela existir; a identidade "consumo exibido igual consumo cobrado" é provada por construção (a política e a rota chamam a mesma função) e por comportamento (a identidade dentro_do_limite_do_aluno === dentro_do_limite(consumo_do_aluno) roda com contagem maior que zero, porque com zero ela é verdadeira à toa); e o decoy de uma rota de ensaio com contagem paralela tem de reprovar.
Aceite (H1): Dado um aluno autenticado, Quando abre /plano, Então vê o nome, a descrição e o status do plano vigente, e as seis dimensões na ordem da lista única, cada uma com o valor máximo, E o consumo exibido em cada dimensão medível vem da mesma função que a política de {F1-15} usa para cobrar, nunca de consulta paralela, E na dimensão ilimitada o lugar do máximo traz a palavra ilimitado, sem barra e sem alerta, ainda que o consumo seja alto, E na dimensão cuja tabela ainda não existe o consumo é exibido como não medido, nunca como zero, E o consumo nunca é limitado ao máximo, um aluno acima do teto por redução de plano vê o número real, com a barra cheia, E o aluno com acesso inativo vê a tela inteira, em leitura, com o aviso único de {F1-14}, E nenhuma escrita, upgrade, cobrança ou botão de mudança de plano existe nesta tela.
Nota: a tela /plano e o estado "conta inativa" com banner âmbar da página 17 são deste card; o painel do Aluno e /perfil receberam em {F1-14} apenas a humanização do status e o aviso, e o texto do aviso vive num só lugar, reusado pelos três.
Nota (F1-15): o consumo desta tela lê pelas mesmas contagens que a política de {F1-15} usa (mfit.dentro_do_limite_do_aluno e o CASE por dimensão), nunca por uma consulta paralela escrita de novo; consumo exibido diferente do consumo cobrado é o defeito clássico desta família.
Fechamento [2026-09-08]: entregue e em produção (03668f4, migração 0016_consumo_do_aluno.sql). Ficou pronto: tela /plano exibindo o consumo pela mesma função que a política de {F1-15} usa (mfit.consumo_do_aluno), mfit.plano_vigente_desde para o campo desde, rota GET /api/meu-plano privativa do aluno, e o décimo primeiro verificador com placar próprio. RF-PLAN-004 fecha. TA-19 fecha 2 de 2 cenários, marcado.
RF-CORE-011.
O que implementar: página sem sessão no endereço raiz, com apresentação do produto, os cinco papéis de público nominados e as chamadas entrar e criar conta; usuário com sessão ativa é levado ao painel do papel. A remoção dos dois links de ferramenta de desenvolvimento da home (/api/estado e /estilo), presentes na home provisória (P2 da Inspeção de 06/09); a fonte única de texto da página (kicker, título, parágrafo do herói, os cinco papéis, os três pilares, cabeçalho e rodapé públicos); o espelho dos quatro rótulos de papel contra CATALOGO_ESPECIALIDADES; e o décimo segundo verificador (ops/verifica-home.mjs), com placar próprio (1 aluno + 4 especialidades = 5 papéis).
Como verificar (teste primeiro): abrir o endereço raiz sem sessão e conferir que não há redirecionamento para a tela de entrada; conferir os cinco papéis e as duas chamadas; abrir o mesmo endereço com sessão ativa de cada papel. A conferência de conteúdo não se faz por requisição sem navegador, porque o servidor devolve o mesmo index.html para qualquer caminho, requisição crua prova só o código de resposta; e o cenário "com sessão ativa" roda com os três papéis (aluno, profissional, administrador), cada um chegando ao painel do próprio papel.
Aceite (H1): Dado um visitante sem sessão, Quando abre o endereço raiz do sistema, Então a página responde com sucesso sem redirecionar para a tela de entrada, E o primeiro bloco da página trata do treino de musculação, antes de qualquer menção a dieta, saúde ou comunicação, E a página exibe os cinco papéis de público nominalmente e as duas chamadas de ação, entrar e criar conta, E cada chamada leva à tela correspondente, E um usuário com sessão ativa que abre o endereço raiz é levado ao painel do papel dele.
Fechamento [2026-09-08]: entregue e em produção. Ficou pronto: home pública no endereço raiz com o treino de musculação como primeiro bloco, os cinco papéis de público nominados, as duas chamadas (entrar, criar conta), o redirecionamento por marcador de sessão para o painel do próprio papel, os dois links de ferramenta de desenvolvimento removidos da home (P2 da Inspeção de 06/09 fechado), e o décimo segundo verificador com placar próprio (1 aluno + 4 especialidades = 5 papéis). RF-CORE-011 fecha. TA-77 fecha 3 de 3 cenários, marcado.
RF-CORE-012, D.6 e ADR-012.
O que implementar: o registro de aceite já existe desde F1-04; este card traz o texto e a versão como configuração editável, os quatro pontos de exibição e a publicação de versão nova sem alterar aceite anterior.
Como verificar (teste primeiro): percorrer os quatro pontos e conferir a presença do texto em cada um; publicar uma versão nova e conferir que os quatro passam a exibi-la e que o aceite anterior continua com a versão antiga.
Aceite (H1): Dado o texto do aviso publicado na versão vigente, Quando um usuário percorre os quatro pontos, Então o aviso aparece nos quatro, E o aceite fica registrado com versão e data, E publicar versão nova troca o texto dos quatro sem alterar o registro do aceite anterior, E remover o aviso de qualquer um dos quatro pontos reprova o card.
Nota [F1-17]: a página pública inicial (RF-CORE-011) não é um quinto ponto deste requisito. Os quatro pontos continuam fixos e nominados (termos do cadastro, cadastro do profissional para as quatro especialidades, topo das quatro telas clínicas, pacote de exportação). A frase de apresentação da home diz o que a plataforma é, em prosa; não consome GET /api/cadastro/aviso-nao-clinico, não registra aceite e não carrega número de versão.
Nota (R-7, fechamento): o card entrega o MECANISMO (tabela versionada, componente único, verificador por critério), não os quatro pontos prontos. Placar honesto: 2 servidos (termos do cadastro, cadastro do profissional) + 2 pendentes (topo das quatro telas clínicas em {F5-01}, {F5-04}, {F5-05}, {F5-06}; pacote de exportação em {F8-09}) = 4. O placar sobe conforme os cards donos nascerem, cada um consumindo <AvisoNaoClinico />.
Fechamento [2026-09-13]: entregue e em produção (42c5637, migração 0017_texto_versionado.sql). Ficou pronto: tabela mfit.texto_versionado (versionada, imutável, RLS ligada), componente único AvisoNaoClinico no front, rota GET /api/textos/:chave/:ponto, painel /admin/textos com edição e publicação de versão nova, décimo terceiro verificador por critério (ops/verifica-aviso-nao-clinico.mjs), textos oficiais semeados (termos de uso versão 2.2, três variantes do aviso não clínico). QA achou e corrigiu o P0 de /privacidade sem o aviso (68e78f0). RF-CORE-012 fecha o MECANISMO, não os quatro pontos: placar 2 servidos (termos do cadastro, cadastro do profissional) + 2 pendentes (telas clínicas em {F5-01}, {F5-04}, {F5-05}, {F5-06}; pacote de exportação em {F8-09}) = 4. TA-85 continua aberto, placar 2 de 4.
RF-CORE-004, RF-CORE-006 e D.2.
O que implementar: o que este card entrega de verdade, medido no gate de plano (R-1 a R-10): o tipo composto mfit.perfil_publico_reduzido (quatro atributos fechados, nome, foto, especialidade e registro) e a função mfit.perfil_publico, únicas portas de leitura reduzida de terceiro; RLS ligada em mfit.usuario, mfit.perfil_aluno e mfit.perfil_profissional, com dez políticas restritas ao titular e ao Administrador, nunca ao vinculado; cinco funções SECURITY DEFINER estreitas (mfit.credencial_por_email, mfit.conta_ativa_por_email, mfit.nome_do_usuario, mfit.papel_do_usuario, mfit.criar_usuario) para os caminhos anônimos por requisito; promoção de claim (set_config dentro da transação, identidade já provada pelo token de uso único) na redefinição de senha e na confirmação de e-mail, cobrindo ao todo os nove caminhos sem claim medidos na Fase 0 do gate; REVOKE DELETE nas três tabelas, para que a exclusão negada seja erro de permissão e não zero linhas; a rota GET /api/perfil/:usuarioId com o ramo do Administrador auditado na mesma transação da leitura; o quarto placar de ops/verifica-acesso-aluno.mjs, por critério nominal (política por linha, conjunto fechado de quatro atributos, varredura estática do server/src proibindo SELECT direto nas três tabelas, os oito campos privados ausentes da resposta HTTP real); e a oitava seção do gabarito, "Perfil público reduzido". A RLS de plano_plataforma foi ligada em {F1-13}; este card apenas a confirma no quarto placar, não a liga de novo. A obrigação herdada de {F1-14} (AND mfit.autor_com_acesso_ativo() no WITH CHECK de perfil_aluno) entra no universo do critério (d) do verificador daquele card, cujo placar sobe de 3 servidas + 17 pendentes = 20 para 4 servidas + 17 pendentes = 21; perfil_profissional e usuario ficam fora desse critério, com motivo escrito (nenhum aluno escreve neles fora do próprio nome).
Como verificar (teste primeiro): consultar o perfil de outro usuário por cada caminho da aplicação e conferir campo a campo o que a resposta traz, procurando nominalmente por telefone, nascimento, sexo, altura, identificador público, plano, status e e-mail; três decoys obrigatórios, cada um visto reprovando antes de ser desfeito (acrescentar telefone ao tipo composto, criar um arquivo com SELECT direto nas três tabelas, remover a política de titular de usuario).
Aceite (H1): Dado dois usuários quaisquer com vínculo entre eles, Quando um consulta o perfil do outro por qualquer caminho da aplicação, Então recebe exatamente nome e foto, mais especialidade e registro quando o outro é profissional, E nenhum dos oito campos privados nominados em D.2 aparece na resposta, E o mesmo pedido feito pelo Administrador retorna o perfil completo com registro de auditoria.
Nota: a política de escrita de perfil_aluno criada aqui carrega AND mfit.autor_com_acesso_ativo() no WITH CHECK, seguindo a seção nova do gabarito de {F1-14}; até este card existir, a negação do PATCH /api/perfil para aluno inativo mora só na camada de API.
Nota (R-3, {TA-06}): a cláusula de perfil do H1 de RF-CORE-006 fecha neste card; as outras três cláusulas (dezesseis entidades, duas restritas, trinta e seis casos) só fecham na Fase 8. O placar N de 36 de {TA-06} não se move aqui: usuario, perfil_aluno e perfil_profissional não estão entre as dezoito entidades de E.1.
Placar honesto do card: 3 tabelas ligadas + 1 confirmada = 4; 4 campos públicos + 8 privados negados; 9 caminhos sem claim tratados; 1 de 4 cláusulas do H1 de {TA-06} fechada; placar N de 36 inalterado. Nunca "RF-CORE-006 fechou": ele fecha na Fase 8.
Nota (R-4, cinco lugares de D.2): a Nota nominal foi publicada nos cards que hoje possuem tela onde dois usuários se encontram: {F2-06} (lista de vínculos), {F7-07} a {F7-10} (carteira), {F6-01} e {F6-02} (conversa), {F6-03} (grupo). O quinto lugar, "autoria de registro" (o nome do autor de uma ficha, plano, prescrição ou sessão exibido a outro usuário), não tem um único card dono hoje: recorre em cada card que grava e exibe autoria (entre outros, {F3-01}, {F3-02}, {F3-04}, {F5-01}, {F5-04}, {F5-05}, {F5-06}); cada um desses, ao nascer, segue a mesma regra (consumir a leitura reduzida, nunca um SELECT próprio), e o quarto placar deste card os pega no dia em que nascerem lendo a tabela na mão.
Fechamento [2026-09-13]: entregue e em produção (635dda0, migração 0018_perfil_publico.sql), com o parâmetro não uuid da rota corrigido em 80b0ac3 antes do merge (P1 do qa). Ficou pronto: o tipo composto mfit.perfil_publico_reduzido e a função mfit.perfil_publico, únicas portas de leitura reduzida de terceiro; RLS ligada em mfit.usuario, mfit.perfil_aluno e mfit.perfil_profissional, com 8 políticas (2 em usuario, 3 em perfil_aluno, 3 em perfil_profissional) mais 3 REVOKE DELETE, nenhuma delas com OR ... IS NULL; as cinco funções SECURITY DEFINER estreitas mais duas promoções de claim resolvendo os nove caminhos sem claim (6 por função definer, 2 por promoção de claim, 1 já coberto por política existente); a rota GET /api/perfil/:usuarioId com o ramo do Administrador auditado na mesma transação da leitura; e o quarto placar de ops/verifica-acesso-aluno.mjs, por critério (política por linha, conjunto fechado de quatro atributos, varredura estática do server/src proibindo SELECT direto nas três tabelas, os oito campos privados ausentes da resposta HTTP real), com os três decoys vistos reprovando antes de serem desfeitos. O critério (d) herdado de {F1-14} sobe de 3 servidas + 17 pendentes = 20 para 4 servidas + 17 pendentes = 21 (perfil_aluno entra no universo, usuario e perfil_profissional ficam fora com motivo escrito). Placar honesto: 3 tabelas ligadas + 1 confirmada = 4; 4 campos públicos + 8 privados negados; 9 caminhos sem claim resolvidos; 1 de 4 cláusulas do H1 de {TA-06} fechada; placar N de 36 inalterado (as três tabelas não estão entre as dezoito entidades de E.1). RF-CORE-006 não fecha aqui, fecha na Fase 8. QA rodou os três papéis mais o anônimo na URL de produção e confirmou, item a item, que login, cadastro, recuperação de senha, confirmação de e-mail e a rotina de e-mail sobreviveram (D-10, passo 6). Gates plan, qa e review verdes.
Definição de Pronto da Fase 1: os trinta e seis casos de isolamento passam, o perfil de terceiro devolve apenas os campos públicos, nenhuma escrita de categoria sensível acontece sem consentimento, o aviso de natureza não clínica está nos quatro pontos, toda escrita produz auditoria e o plano governa os limites por configuração.
Fase 2, Catálogo de exercícios e vínculo
Nota: a tabela vinculo (estrutura e índices) nasce em {F1-08}, junto do auxiliar de acesso que decide por ela. Esta fase entrega a máquina de estados, as rotas de convite, aceite, recusa e encerramento, as notificações e as telas sobre essa tabela.
RF-EXER-001.
Aceite (H1): Dado o Administrador na tela de exercícios, Quando cria um exercício com os onze campos do apêndice, incluindo erros comuns e dicas de execução, Então ele aparece na busca de todos os usuários e a tela do exercício exibe os dois campos novos como blocos próprios abaixo das instruções, E um exercício com os dois vazios não exibe bloco vazio, E toda operação de criação, edição ou exclusão por papel diferente de Administrador é negada.
Nota (R-1): a semente prometida (docs/seed/exercicios.csv e docs/seed/midia/) com os 113 exercícios do protótipo ainda não chegou. Este card entrega o mecanismo completo de manutenção do catálogo mais o importador idempotente (ops/importar-exercicios.mjs), e abre com o placar honesto 0 de 113 exercícios importados. O mapeamento de colunas segue o esquema medido do protótipo até o README da pasta chegar; nenhum exercício é inventado, e nenhum é redigitado à mão.
Fechamento [2026-09-13]: merge f3addf5 na main, deploy provado em produção (https://fit.modulareasy.com, versão f3addf5); migração 0019 aplicada (tabela mfit.exercicio, três enums, três índices, quatro políticas, três gatilhos de auditoria reaproveitados, extensão pg_trgm); gates plan, qa e review verdes (qa PASS após correção de dois achados P0 de contrato em 608633c); décimo quarto verificador (ops/verifica-exercicios.mjs) registrado. Placar da semente: 0 de 113 exercícios importados, porque docs/seed/exercicios.csv do dono não existe (pendência dele, fora do escopo de código, sem bloquear o card). Telas entregues: /admin/exercicios, /exercicios, /exercicios/:id.
Nota (fechamento da semente, 2026-09-14): docs/seed/exercicios.csv chegou (commit 53cb783, branch feat/semente-exercicios, merge na main pendente nesta data); ops/importar-exercicios.mjs --aplicar rodou em produção: 113 de 113 importados, 0 atualizados, 0 recusados, confirmado por contagem direta no banco (113 exercícios ativos). Placar da semente passa de 0 de 113 para 113 de 113. A etapa de mídia de {F2-02} continua 0 de N, docs/seed/midia/ ainda ausente.
RF-EXER-002.
Como verificar (teste primeiro): medir a proporção renderizada e comparar com a proporção natural do arquivo, não julgar a olho.
Aceite (H1): Dado um exercício com imagem enviada e endereço de vídeo, Quando o aluno o abre durante a sessão, Então a imagem é exibida na proporção natural e o vídeo aparece embutido e reproduzível, E um exercício sem mídia exibe o espaço reservado sem alterar a altura dos demais elementos.
Nota (R-13): o card entrega envio de imagem pela administração com guarda de tamanho e formato por bytes; endereço externo de imagem aceito; vídeo por endereço de lista fechada, embutido e reproduzível; dimensão natural medida no servidor; exibição por aspect-ratio sem deslocamento; retângulo reservado quando não há mídia; middleware único de cabeçalhos de segurança; importador estendido para docs/seed/midia/ abrindo em 0 de N porque a pasta não existe (medido); décimo quinto verificador, o primeiro com navegador. Placar: 13 de 13 campos do formulário, 1 de 2 pontos de exibição de mídia (execução da sessão pendente em {F3-05}), 1 de 2 repositórios com escrita, 27 parâmetros de operação. {TA-21} fica aberto.
Fechamento [2026-09-13]: entregue e em produção (merge 9d326d0 na main, https://fit.modulareasy.com); migração 0020 aplicada (duas colunas de dimensão em mfit.exercicio, zero política e zero gatilho novos, as quatro políticas e os três gatilhos de {F2-01} já cobriam); gates plan, qa e review verdes (qa aprovado na rodada 3, após BUG-01, BUG-02 e o achado do DELETE). Ficou pronto: middleware único de cabeçalhos de segurança emitido pela própria aplicação, não pelo vhost do servidor web da frente; imagem por arquivo enviado ou por endereço externo, na mesma coluna; vídeo sempre por endereço, lista fechada de serviços; as duas colunas de dimensão medidas no servidor a partir dos bytes do arquivo, alimentando aspect-ratio sem deslocamento; retângulo reservado quando não há mídia; décimo quinto verificador (ops/verifica-midia-exercicio.mjs), o primeiro com navegador, medindo a proporção renderizada por getBoundingClientRect() contra naturalWidth/naturalHeight, tolerância 0,5 por cento. Placar honesto: 13 de 13 campos do formulário, 1 de 2 pontos de exibição de mídia (execução da sessão pendente em {F3-05}), 1 de 2 repositórios com escrita, 27 parâmetros de operação, semente de mídia do dono (docs/seed/midia/) ausente, placar 0 de N. {TA-21} continua aberto.
RF-EXER-003.
Aceite (H1): Dado um catálogo com exercícios de dez grupos musculares, Quando o usuário filtra por grupo Costas e dificuldade Iniciante, Então a lista contém somente itens que atendem às duas condições, E o contador exibido é igual ao número de itens listados.
Nota (R-10, {F2-01}): {F2-01} já entrega a busca por texto no nome mais o contador, na mesma rota e na mesma consulta, porque o H1 de RF-EXER-001 exige que o exercício apareça na busca de todos os usuários e uma lista sem contador não se verifica. Este card não reconstrói a rota nem o índice: acrescenta os quatro filtros combináveis (grupo, dificuldade, categoria, equipamento) na MESMA rota e na MESMA consulta, e o contador continua sendo o mesmo número da lista, nunca uma segunda contagem.
Fechamento [2026-09-14]: merge 05d52cf na main, deploy provado em produção (https://fit.modulareasy.com, versão 05d52cf); sem migração, sem índice novo (o exercicio_grupo_dificuldade_idx existente já serve os dois filtros do H1 e os quatro filtros mais a busca por Bitmap Index Scan, medido por EXPLAIN ANALYZE em 5.000 linhas sintéticas, 44 vezes o volume real, dentro de transação desfeita: 0,242 ms com grupo mais dificuldade, 0,144 ms com os quatro filtros mais o LIKE); gates plan, qa e review verdes. Ficou pronto: grupo, dificuldade, categoria e equipamento como parâmetros opcionais e combináveis da MESMA rota GET /api/exercicios, validados contra os conjuntos fechados de catalogo.ts (400 parametro_invalido por campo, nunca uma segunda lista de valores), equipamento como texto livre de comparação exata com o vocabulário vindo do próprio dado; totalAtivos calculado na mesma janela da consulta (count(*) FILTER (WHERE ativo) OVER()), nunca uma segunda consulta nem contagem no cliente; tamanhoPagina lido da resposta do servidor nas duas telas. Verificador npm run verificar:exercicios rodou 15 de 15 no critério (h), aprovado, zero problemas. Dois drifts corrigidos antes do H1 fechar: a paginação pública usava uma constante local (ITENS_POR_PAGINA=20) desalinhada com o tamanhoPagina=24 do servidor, e o cabeçalho da administração contava itens.filter(...).length sobre a página carregada em vez do universo do catálogo; os dois passaram a ler o número do servidor, sem placar novo de RF (paginação é D.1, já publicada). {TA-22} fecha. Construção sobe de 23 de 96 para 24 de 96.
RF-EXER-004.
Aceite (H1): Dado um aluno no plano gratuito e sem vínculo, Quando abre o catálogo, Então consulta qualquer exercício com nome, mídia e instruções, E nenhuma ação de escrita é oferecida nem aceita para ele.
Fechamento [2026-09-14]: entregue e em produção (merge 98db15a na main, deploy provado em https://fit.modulareasy.com, versão 98db15a); sem migração; gates plan, qa e review verdes. Ficou pronto: três pontos de entrada por interface, o link Exercícios nos painéis do aluno, do profissional e do administrador (único gap funcional medido, a tela já existia desde {F2-01}); décimo quarto verificador com dois critérios novos, (j) 0 de 6 dimensões de plano tocam o catálogo (ausência gravada como medida, não como zero) e (k) 5 de 5 rotas que mutam mfit.exercicio sob /api/admin com o papel conferido antes do banco, por varredura de server/src/rotas; décimo quinto verificador com sessão real de aluno navegando /exercicios e o detalhe sem encontrar nenhum controle de escrita; arnês leituraPorPapel.test.ts com aluno criado pela rota real (Gratuito, 0 vínculos), lendo lista, detalhe e mídia, cinco negações para aluno e profissional com controle positivo de administrador. {TA-23} fecha, {TA-21} continua aberto (pendente da execução da sessão em {F3-05}). Construção sobe de 24 de 96 para 25 de 96.
RF-VINC-001.
Aceite (H1): Dado um profissional autenticado e um aluno sem vínculo com ele, Quando envia o convite pelo identificador do aluno, Então nasce um vínculo pendente, E o aluno recebe a notificação, E toda consulta do profissional a dado desse aluno retorna vazia enquanto pendente.
Nota (achado do code-review em F1-08): a política vinculo_leitura hoje usa pode_ler_aluno(aluno_id), que responde por vínculo ATIVO com o aluno, sem amarrar à linha de vínculo específica sendo lida. Ao construir a máquina de estados deste card, a leitura de vinculo deve passar a profissional_id = mfit.usuario_atual() OR aluno_id = mfit.usuario_atual() OR papel administrador, e não pode_ler_aluno(aluno_id) genérico: hoje um profissional com vínculo ativo lê também os convites pendentes do aluno com outros profissionais, e ver os próprios vínculos não é ver dado clínico do aluno.
Nota (F1-15): o convite nasce pendente e pendente não conta limite, a guarda de RF-PLAN-003 é na entrada em ativo; além disso a recusa por par já existente é 23505 do índice único parcial, uma recusa diferente da de limite, com mensagem própria, e o tradutor único de {F1-15} só age sobre o erro de permissão.
Nota (gate {F2-05}, achado de planejamento): a Descrição de RF-VINC-001 permitia busca "ou pelo nome" no DRS; reconciliado, pois {ART-ACS-11} e D.2 nunca aceitam nome nem LIKE, e o H1 já citava só o identificador. O convite se faz somente pelo identificador que o aluno dita ou copia do próprio perfil.
Nota (gate {F2-05}, transição sem dono): E.3 lista seis transições do vínculo; pendente para cancelado (profissional cancela o próprio convite) não aparece em H1 de nenhum card da Fase 2 até aqui, embora o mockup preveja a ação "Cancelar" na carteira. Este card não a constrói; o dono fica a nomear entre {F2-06} e {F2-07} no fechamento da fase.
Fechamento [2026-09-14]: merge 7eaa87b na main, deploy provado em produção (https://fit.modulareasy.com, versão 7eaa87b); migração 0021_convite_de_vinculo.sql; gates plan, qa e review verdes. Ficou pronto: a política vinculo_leitura passa de pode_ler_aluno(aluno_id) para "meus vínculos" (profissional dono da linha, aluno titular ou Administrador), com a perda declarada e única, o profissional com vínculo ativo deixa de ler as linhas de vínculo do aluno com terceiros; mfit.usuario_por_identificador_publico, sexta função SECURITY DEFINER estreita, resolve o identificador MF-XXXX-XX por igualdade exata e devolve só o uuid; gatilho AFTER INSERT ON mfit.vinculo WHEN (NEW.estado = 'pendente') enfileira a notificação de convite por e-mail na origem, garantindo por construção que todo caminho que cria vínculo pendente notifica; POST /api/vinculos (identificador no corpo, especialidade sempre do perfil) e GET /api/vinculos no arquivo já existente; tela /alunos mínima, com o convite e a lista de pendentes enviados. Décimo sexto verificador (ops/verifica-vinculo.mjs) com quatro placares: 3 de 3 políticas restritas às partes mais Administrador, 1 de 6 transições servidas, 27 de 27 pontos que criam vínculo pendente notificam por construção, pendente 0 linha e ativo 1 linha no lado negativo do acesso. {TA-11} fecha. Construção sobe de 25 de 96 para 26 de 96.
RF-VINC-002.
Aceite (H1): Dado um vínculo pendente, Quando o aluno aceita, Então o vínculo fica ativo e o profissional enxerga os dados nas categorias consentidas, E quando recusa, o vínculo fica recusado e nenhuma consulta do profissional retorna registro.
Nota (F1-15): o aceite é um UPDATE de pendente para ativo e já nasce coberto pela política de {F1-15}; não escrever uma segunda verificação de limite no tratador, envolver a escrita em SAVEPOINT e chamar traduzirNegacaoDeEscrita, que devolve a mensagem que RF-PLAN-003 exige.
Nota (F1-16): nada muda; a guarda e a mensagem continuam as de {F1-15}.
Nota (R-4, {F1-19}): a lista de vínculos do aluno (tela de vínculo pendente e ativo, com nome e foto do profissional do outro lado) consome GET /api/perfil/:usuarioId (ou a função perfilPublicoReduzido, quando a leitura for no servidor); proibido SELECT próprio em mfit.usuario, mfit.perfil_aluno ou mfit.perfil_profissional. O quarto placar de ops/verifica-acesso-aluno.mjs reprova o arquivo novo que ler a tabela na mão, e a política por linha já nega telefone, nascimento, sexo, altura, identificador público, plano, status de acesso e e-mail.
Nota (gate {F2-06}, escopo reconciliado em 2026-09-14): entrega o aceite e a recusa pelo aluno no bloco Vínculos de /perfil (tela 23), sem rota /vinculos nova; GET /api/vinculos passa a servir os dois papéis; a notificação ao profissional nasce por gatilho na origem, irmão do de {F2-05}; a auditoria da transição nasce por critério (WHEN estado mudou AND NEW.estado <> 'inativo'), ação alterar, sem valor de enum novo; o placar de transições da máquina de estados fecha em 3 de 6 servidas ou 4 de 6 se a prova de reconvite rodar, sempre medido; e a cláusula "categorias consentidas" do H1 se prova hoje por mfit.consentimento e mfit.pode_ler_aluno, com o placar honesto 0 de 6 tabelas de categoria sensível (nascem na Fase 5).
Fechamento [2026-09-14]: merge 9b994b0 na main, deploy provado em produção (https://fit.modulareasy.com, versão 9b994b0); migração 0022_aceite_e_recusa_do_vinculo.sql; gates plan, qa e review verdes. Ficou pronto: aceite e recusa como UPDATE condicional dentro de SAVEPOINT, guardado por aluno_id = claims.sub AND estado = 'pendente' no próprio WHERE, sem segunda verificação de limite fora da política de {F1-15}; notificação ao profissional por gatilho na origem, irmão do de {F2-05} (AFTER UPDATE OF estado ... WHEN (OLD.estado = 'pendente' AND NEW.estado IN ('ativo','recusado'))); auditoria da transição por critério (WHEN OLD.estado IS DISTINCT FROM NEW.estado AND NEW.estado <> 'inativo'), ação alterar, sem valor de enum novo; GET /api/vinculos passa a servir os dois papéis, conserto e não feature, o aluno recebia lista vazia com 200; bloco Vínculos nasce em /perfil (tela 23), sem rota /vinculos nova; /alunos do profissional passa a exibir o estado real da linha. Décimo sexto verificador (ops/verifica-vinculo.mjs), dois critérios mais exigentes: máquina de estados com o placar 4 de 6 transições servidas, mais o lado negativo novo, as duas transições proibidas de E.3 com o caminho de escrita medido que as alcança hoje (POST /api/vinculos/:id/desativar), dono nomeado {F2-07}; notificação exaustiva pelos dois gatilhos (0021 e 0022). Arnês com banco real prova as duas cláusulas do H1 com controle positivo e negativo, inclusive a jornada completa do limite. {TA-12} fecha; {TA-18} fecha com a jornada completa (recusa por limite, ampliação, ilimitado). pendente para cancelado e a transição proibida hoje alcançável pela rota de desativar seguem nomeadas para {F2-07} (entregue). Construção sobe de 26 de 96 para 27 de 96.
RF-VINC-003.
Aceite (H1): Dado um vínculo ativo com três fichas criadas pelo profissional, Quando qualquer um dos dois encerra, Então o vínculo fica inativo, E as três fichas continuam existindo com a autoria preservada e visíveis ao aluno e ao Administrador, E toda criação ou edição pelo profissional nesse aluno é negada.
Nota (F1-15): encerrar vínculo nunca é recusado por limite, mesmo com o aluno acima do teto, a guarda estado = 'ativo' da conjunção de {F1-15} garante isso; e o Apêndice manda que reduzir o plano não apague nada.
Nota (gate {F2-06}, dono nomeado em 2026-09-14): este card é o dono de pendente para cancelado (o botão "Cancelar" do convite pelo profissional, tela 25 do mockup), decidido pelo critério do ATOR: {F2-06} serve as duas transições do aluno (aceitar, recusar), {F2-07} serve as de encerramento, inclusive esta, que é do profissional sobre o próprio convite. Medição que este card herda: POST /api/vinculos/:id/desativar ({F1-10}) hoje aceita um vínculo pendente e o leva a inativo (WHERE id = $1 AND estado <> 'inativo'), produzindo a transição pendente para inativo que E.3 declara proibida; o conserto dessa rota é deste card, e o verificador (item b) imprime as duas transições proibidas de E.3 e o caminho de escrita medido que as alcança até o conserto entrar.
Fechamento [2026-09-14]: merge 42cd973 na main, deploy provado em produção (https://fit.modulareasy.com, versão 42cd973); migração 0023_encerramento_do_vinculo.sql; gates plan, qa e review verdes. Ficou pronto: gatilho BEFORE UPDATE OF estado (mfit.validar_transicao_vinculo) valida a tabela de E.3 na origem, recusando pendente para inativo e ativo para pendente com P0001, nunca só pelo WHERE das rotas; nenhuma rota /encerrar nasce, POST /api/vinculos/:id/desativar consertado e restrito a ativo, mais a rota nova POST /api/vinculos/:id/cancelar (pendente para cancelado, profissional dono ou Administrador); perda declarada e devolvida, o Administrador sai de /desativar num convite pendente e entra em /cancelar; notificação das duas partes por gatilho na origem (mfit.enfileirar_saida_de_vinculo, SECURITY DEFINER), encerramento notifica os dois lados e cancelamento notifica o aluno; e-mail de cancelamento corrigido no review antes do merge. Arnês com banco real (encerramento.test.ts, 22 casos) prova o H1 pelos dois lados, aluno e profissional, em tabela de ensaio mfit.ficha_ensaio. {TA-13} fecha. Décimo sexto verificador: placar de transições em 6 de 6, placar de notificação em 6 de 6. Construção sobe de 27 de 96 para 28 de 96.
RF-VINC-004, RF-PLAN-003 e E.3.
O que implementar: nenhuma guarda por especialidade na criação nem no aceite do vínculo; a única recusa é a do limite de vínculos do plano; unicidade apenas do par aluno e profissional em estado ativo ou pendente.
Como verificar (teste primeiro): conferir no banco que não existe índice nem verificação por aluno e especialidade; aceitar dois vínculos da mesma especialidade num plano ilimitado.
Aceite (H1): Dado um aluno num plano com vínculos ilimitados e já vinculado a um Personal Trainer, Quando aceita o convite de um segundo Personal Trainer, Então passa a ter dois vínculos ativos na mesma especialidade e os dois enxergam os dados consentidos, E aceitar Médico, Fisioterapeuta e Nutricionista leva o total a cinco sem recusa nenhuma, E a única recusa possível é a do limite do plano, E um convite do mesmo profissional que já tem vínculo ativo com o aluno é recusado por já existir aquele par.
Nota (F1-15): a única recusa por quantidade é a do limite de vínculos; a recusa por par repetido é outra coisa (índice único, 23505); e nenhuma guarda por especialidade nasce, nem no WITH CHECK.
Fechamento [2026-09-14]: merge 25e96ad na main, deploy provado em produção (https://fit.modulareasy.com, versão 25e96ad); nenhuma migração nova, última continua 0023; zero linha de código de produto tocada, este card é PROVA. Ficou pronto: critério (e) novo em verificarVinculos.ts mais placar em ops/verifica-vinculo.mjs, por universo de catálogo, nunca varredura de palavra, com decoy obrigatório de seis trechos (quatro que reprovam, dois que passam). Placar de ausência: 0 de 15 objetos de mfit.vinculo usam especialidade como guarda (1 restrição, 3 índices, 4 políticas, 7 gatilhos); 0 de 5 caminhos de escrita em mfit.vinculo aplicam guarda por especialidade. Placar positivo, dentro da mesma transação: 4 de 4 especialidades do catálogo aceitam vínculo ativo simultâneo no mesmo aluno, 2 vínculos ativos de personal_trainer no mesmo aluno, 5 vínculos ativos no total, contagem do limite = 5; teto de exibição medido e impresso, 50 linhas por consulta, sem paginação (Drift 3, dono {F7-07}). Arnês server/src/vinculos/simultaneos.test.ts (16 casos), rotas reais, e-mails com MX, transação desfeita: as quatro metades de {TA-14} providas (cinco vínculos pelas rotas reais sem recusa por especialidade; par repetido recusado com 409 vinculo_ja_existe; plano numérico até estourar com a mensagem que nomeia o limite; conferência no banco aplicado, zero predicado sobre especialidade nos quinze objetos). Décimo sexto verificador: placar de transições inalterado, 6 de 6; medição idêntica reproduzida contra a produção. Construção sobe de 28 de 96 para 29 de 96.
RF-VINC-005.
Aceite (H1): Dado o Administrador na tela de vínculos, Quando filtra por estado pendente, Então a lista mostra somente pendentes com profissional, aluno, especialidade e data, E ao desativar um vínculo o profissional deixa de acessar aquele aluno na consulta seguinte.
Nota ({F2-08}): filtro de listagem por especialidade nesta tela não é guarda de vínculo, que RF-VINC-004 proíbe por desenho; o critério (e) do verificador ops/verifica-vinculo.mjs (núcleo em verificarVinculos.ts) reprova quem confundir os dois.
Nota (gate {F2-09}, escopo reconciliado em 2026-09-14): entra a listagem global dos vínculos com os quatro filtros (estado, especialidade, profissional e aluno) e paginação por chave, a tela /admin/vinculos com o ponto de entrada no menu da administração, e as três ações pelas rotas que já existem (desativar vínculo ativo, cancelar convite pendente e excluir em definitivo), sem rota de ação nova, sem política, sem gatilho e sem valor de enum novo. Não entra "Ativar" e não entra "Novo vínculo" pelo Administrador: E.3 não tem evento de ativação por terceiro, o único caminho até ativo é o aceite do aluno (RF-VINC-002), e o gatilho de validação de transição confere o par de estados e não o ator, então hoje é a ausência do botão que sustenta a regra. Se o botão existisse, a notificação de aceite diria "vínculo aceito" sem que o aluno tivesse aceitado, e a trilha registraria a alteração sem dizer por que alguém que não é parte mudou o estado. A Descrição de RF-VINC-005, a linha do Apêndice de telas, esta jornada e a tela 33 do mockup prometiam "Ativar"; a linha do Apêndice, a jornada e a Nota do mockup foram reconciliadas para o que a máquina sustenta, o requisito NÃO foi editado, e "Ativar" e "Novo vínculo" continuam sem card dono, como decisão de produto a confirmar com o dono, porque abrir acesso a dado de aluno sem ato do aluno é decisão de privacidade (D.2, RN-003).
Nota (gate {F2-09}, Drift 4, 2026-09-14): GET /api/vinculos hoje devolve 200 com lista vazia ao Administrador, porque o núcleo decide por papel e qualquer papel que não seja aluno cai no ramo do profissional, cujo filtro é o próprio identificador. É o terceiro papel da mesma classe de defeito que {F2-06} consertou quando o aluno recebia lista vazia com 200, e este card é o dono do conserto: a rota passa a recusar o Administrador nomeando a porta certa, e a resposta ao aluno e ao profissional continua idêntica, campo a campo. Fechamento [2026-09-14]: merge 0668358 na main, deploy provado em produção (https://fit.modulareasy.com, versões 97f3760 e 7523ed9 durante o card, final 0668358); migração 0024_gestao_global_de_vinculos.sql (índice vinculo_criado_em_id_idx e COMMENT da função mfit.perfil_publico, nada além); gates plan, qa e review verdes. Ficou pronto: nenhuma rota de ação nova, as três ações (desativar, cancelar, excluir) continuam pelas rotas que já existiam; rota nova GET /api/admin/vinculos, listagem global paginada por chave com os quatro filtros (estado, especialidade, profissional, aluno), nomes resolvidos por LEFT JOIN LATERAL mfit.perfil_publico, sem citar as três tabelas de perfil; tela /admin/vinculos com Desativar, Cancelar e Excluir, cada uma com confirmação nominal, sem botão Ativar e sem Novo vínculo (decisão A do mds-architect: E.3 não tem evento de ativação pelo Administrador, RF-VINC-002 dá o aceite ao aluno; "Ativar" e "Novo vínculo" seguem sem card dono, como decisão de produto a confirmar com o dono). Perda declarada e única: GET /api/vinculos passa a devolver 403 negado ao Administrador em vez do 200 com lista vazia que nenhum consumidor usava; aluno e profissional recebem os mesmos campos de antes. Critério (c) do quarto placar de ops/verifica-acesso-aluno.mjs ganha lista nominal de dois chamadores autorizados de mfit.perfil_publico; filtro por especialidade provado, no arnês, que não é guarda (o vínculo escondido pela lista continua ativo e continua dando acesso). Arnês server/src/rotas/admin/vinculos.test.ts (unitário, client falso) e server/src/vinculos/gestaoGlobal.test.ts (banco real) provam as duas metades de {TA-15}: filtro por estado pendente trazendo só pendentes, com a aritmética das linhas restantes fechando contra o total sem filtro; e a desativação pelo Administrador, com a sessão do profissional aberta antes, sem novo login, lendo zero depois, mais o controle negativo de um segundo profissional que continua lendo o aluno. {TA-15} fecha. Construção sobe de 29 de 96 para 30 de 96, e a Fase 2 fecha.
Definição de Pronto da Fase 2 (fechada em 2026-09-14): o catálogo global existe com os onze campos e é lido por todos ({F2-01} a {F2-04}); o vínculo governa o acesso do profissional ({F2-05} a {F2-07}, e {F2-09} prova o mesmo pelo caminho do Administrador); o aluno acumula vínculos sem limite por especialidade ({F2-08}); a máquina de estados recusa as transições proibidas ({F2-07}, gatilho BEFORE UPDATE de 0023).
Onda AUD, correções da Inspeção (14/09/2026, entram ANTES de {F3-04})
Achados medidos em produção pela Sessão de Inspeção e pelo próprio executor durante {F3-01}. Códigos imutáveis. Relatório de origem: docs/RECADOS-DO-FISCAL.md (14/09) e .claude/gates/qa/fix-origem-atras-do-proxy.gate. A correção do nginx.conf da máquina (confiança em X-Real-IP de qualquer origem) já foi aplicada pela Inspeção em 14/09 e fica fora deste roadmap por ser infraestrutura compartilhada. {AUD-01} e {AUD-02} já entraram corrigidos pela migração 0025_ficha_de_treino.sql (funções mfit.negacao_por_limite_do_aluno e mfit.acesso_do_aluno_ativo), mergeada em {F3-01} e em produção em 76bc9cd; por isso não têm card nesta onda.
RF-CORE-008, RN-008, D.6.
O que implementar: remover em definitivo, na ordem das chaves estrangeiras, as contas inspecao-qa-20260906@modulareasy.com, inspecao-visual-20260907@modulareasy.com, inspecao-qa2-aluno-20260914@modulareasy.com e as duas inspecao-qa3-*-20260914@modulareasy.com (criadas pelo QA da Inspeção em 14/09), com os vínculos, consentimentos, fichas, auditoria e e-mails associados, registrando a contagem removida por tabela no CHANGELOG; acrescentar ao ops/ um comando limpar-contas-de-teste --prefixo inspecao- reutilizável pelo Administrador.
Como verificar (teste primeiro): contar linhas por tabela antes e depois; select count(*) from mfit.usuario where email like 'inspecao-%' igual a zero; curl https://fit.modulareasy.com/api/estado 200.
Aceite (H1): Dado o banco de produção com as cinco contas de teste, Quando o comando roda com o prefixo, Então zero usuários com o prefixo permanecem, E nenhuma linha órfã fica em vínculo, consentimento, ficha, auditoria ou e-mail, E as contas reais e as três de topo (wladimir, c3po, r2d2) permanecem intactas, E o comando recusa rodar sem prefixo.
Nota (gate {AUD-03}, reconciliado em 2026-09-14): RN-008 nunca autoriza apagar entrada de auditoria (ela é mantida, anonimizada); o texto acima cumpre isso por dois verbos: apaga o que deixa de existir (usuário, perfis, aceite de termos, sessão, token, vínculo, consentimento, ficha, solicitação, e-mail) e anonimiza (autor_id e aluno_id postos a NULL, o resto da linha permanece) a entrada de auditoria cuja referência parte de linha que sobrevive; o comando ainda grava, na mesma transação, uma entrada excluir por conta removida com origem = 'ops/limpar-contas-de-teste', atendendo D.6. Medido em 14/09: o alvo são 4 contas, não 5 (inspecao-qa2-aluno-20260914@ já havia sido removida pelo qa de {F3-02}); auditoria.entidade_id não tem chave estrangeira, e 5 entradas hoje apontam para linhas de vinculo que saem com o alvo. A execução em produção espera o merge de {F3-04} e a limpeza das contas qa.f304.* do qa dele, banco compartilhado com sessão em voo em 14/09.
Fechamento [2026-09-15]: merge 8daaf7d na main, executado em produção depois de backup lógico (pg_dump -Fc, /opt/modularfit/backups/); gates plan, qa e review verdes. Medido no dia: apenas duas das contas nomeadas no card ainda existiam, inspecao-qa-20260906@modulareasy.com e inspecao-visual-20260907@modulareasy.com (as demais já haviam sido removidas por sessões anteriores, ver Nota acima); ops/limpar-contas-de-teste --prefixo inspecao- --aplicar removeu as duas em ordem derivada das chaves estrangeiras, com relatório por tabela (antes, apagadas, anonimizadas, depois) e a aritmética fechando em todas as linhas; a auditoria ganhou duas entradas excluir, origem ops/limpar-contas-de-teste, atendendo D.6; segunda rodada removeu zero contas. As quatro contas reais (wladimir, c3po, r2d2 e aluno de teste) permanecem intactas. Onda AUD fica fora do denominador de 96 da Construção, por não ser uma das nove fases do Mapa de cobertura: este fechamento não move a barra, que permanece 34 de 96, 35,42 por cento.
RF-CORE-007, RF-CORE-008, RF-CORE-009, RF-PLAN-001, RF-PLAN-002, RF-PAIN-009, RN-010, ADR-017 e os blocos B.1, B.4, B.5 e B.6 do DRS. A escada de acesso passa a ter dois papéis de administração: o Super Administrador, de quem opera a plataforma, e o Administrador, da operação do dia.
O que implementar: (1) migração do tipo papel_usuario acrescentando o valor de super administrador, sem remover nenhum valor existente; (2) migração de dados promovendo a super administrador exatamente as três contas de topo da plataforma, medidas na data do card, e nenhuma outra, com a mudança de papel gravada na trilha de auditoria como qualquer outra; (3) revisão de TODA política de acesso por linha, função auxiliar e guarda de rota que hoje compara o papel com o literal de administrador, agora em TRÊS classes e não duas: onde a capacidade é comum aos dois papéis, a comparação passa a ser contra a lista dos dois; onde a capacidade é uma das sete privativas de governança de B.1, a comparação passa a ser apenas contra super administrador; e onde a entidade pertence à fronteira de dado clínico (as quatorze capacidades de B.1, dez por classificação e quatro por conteúdo, estas últimas alcançando conversa, mensagem, conta de integração, atividade externa e o acionamento da geração assistida), a comparação também passa a ser apenas contra super administrador, valendo para LEITURA e para escrita, o que exige que o auxiliar único de decisão de acesso (ART-ACS-07) passe a receber a entidade consultada e a lista das entidades da fronteira viva num lugar só; (4) telas de administração divididas conforme o Apêndice de catálogo de telas, sete rotas para os dois papéis, /admin/auditoria só para o Super Administrador e /admin/planos com escrita do Super Administrador e leitura do Administrador; (5) na tela de usuários, as quatro ações privativas deixam de ser oferecidas ao Administrador na interface E são negadas no servidor, sendo a interface consequência da negação e nunca o controle; (6) semente idempotente das três contas de topo no padrão de RN-010, com o registro de quem é quem e a senha vigente no arquivo de credenciais do projeto, fora do repositório.
Como verificar (teste primeiro): contar, antes da migração, quantas contas têm papel de administrador e quais são, e conferir depois que exatamente essas três viraram super administrador e que a contagem de cada um dos demais papéis não mudou; varrer o código e o catálogo do banco por toda comparação com o literal de papel de administrador e fechar o placar nas três classes acima, comum, privativa de governança e fronteira de dado clínico, sem sobra; entrar com uma conta de Administrador criada para o teste e exercitar, uma a uma, as capacidades privativas de governança que já têm rota nesta data, esperando negação em cada uma e fechando 4 de 7: atribuir plano e status de acesso, escrever na tela de planos, excluir em definitivo e abrir a trilha de auditoria; as três restantes não têm rota nenhuma, e por isso a negação delas se prova no banco, pelo gatilho de governança que este card instala, com a negação por rota nascendo nos cards donos, bloquear e desbloquear conta e promover ou rebaixar papel em {F7-14} e personificar em {AUD-05}; pedir, com essa mesma conta, leitura e escrita em cada uma das quatorze capacidades da fronteira, pela tela E pela rota direta, esperando negação nas vinte e oito tentativas, sem conteúdo parcial e sem lista vazia que se confunda com resultado legítimo (a diferença entre negado e vazio se prova criando o dado com outra conta antes de pedir, e as quatro capacidades de conteúdo exigem conversa, atividade importada e proposta gerada já existentes no banco); exercitar as rotas comuns que já existem e o dado não clínico do mesmo aluno, inclusive as preferências de treino, que ficam com ele, esperando sucesso e fechando 7 de 10 do lado positivo, porque das sete rotas comuns existem quatro nesta data (/admin, /admin/exercicios, /admin/vinculos e /admin/textos) e as três restantes nascem nos cards donos, /admin/usuarios em {F7-14}, /admin/relatorios em {F7-13} e /admin/guia em {F9-03}; contar as células diferentes de N por coluna na matriz do DRS e conferir 41 para o Super Administrador e 20 para o Administrador, com as 21 células divergentes enumeradas nominalmente e não só subtraídas.
Aceite (H1): Dado o banco migrado, Quando se conta o papel de cada conta, Então exatamente as três contas de topo têm papel de super administrador e nenhuma outra conta teve o papel alterado, E um Administrador criado para o teste entra, alcança as rotas de administração comuns que já existem, lê a tela de planos e opera o dado não clínico de qualquer aluno, fechando 7 de 10 do lado positivo, com as três que faltam nomeadas com o card dono (/admin/usuarios em {F7-14}, /admin/relatorios em {F7-13} e /admin/guia em {F9-03}), E as capacidades privativas de governança de B.1 que já têm rota são negadas a ele pela rota e pela política de acesso por linha, e não apenas escondidas na interface, fechando 4 de 7, com as três que faltam nomeadas com o card dono (bloquear e desbloquear conta e promover ou rebaixar papel em {F7-14}, personificar em {AUD-05}) e a negação das duas primeiras provada nesta data no banco, pelo gatilho de governança, E as quatorze capacidades da fronteira de dado clínico negam a ele leitura e escrita, fechando 28 de 28 tentativas negadas com dado existente no banco, E as preferências de treino continuam alcançáveis por ele, prova de que a fronteira é a lista de B.1 e não uma impressão de sensibilidade, E a contagem de capacidades por papel devolve 41 e 20, diferença de 21 que se decompõe em 7 mais 14, E a semente das três contas rodada duas vezes seguidas não cria conta repetida, não rebaixa papel e não sobrescreve senha já trocada.
Perda declarada, e ela tem duas metades. Primeira metade, governança: o papel de Administrador, que até aqui era o topo, deixa de poder sete coisas que podia, que são atribuir plano e status de acesso, manter o catálogo de planos e os limites operacionais, bloquear e desbloquear conta pela administração, promover ou rebaixar papel e criar conta de Administrador, personificar usuário, excluir em definitivo e consultar a trilha de auditoria. Segunda metade, dado clínico: ele deixa de LER e de escrever anamnese, avaliação física, medida corporal, foto de progresso, exame, prescrição, sessão de reabilitação, refeição, meta nutricional e plano alimentar de aluno nenhum, em unidade nenhuma, e deixa também de ler as conversas do aluno, de operar e ler as integrações de atividade dele e de acionar a geração assistida, quatro capacidades que não são categorias de saúde mas transportam o dado delas. Preferências de treino ficam com ele (RN-001). Consequência operacional que se declara em vez de se descobrir depois: o chamado de suporte que toca saúde, dieta, evolução, conversa ou relogio passa a exigir escalada ao Super Administrador, que lê sob o consentimento já dado (RF-CORE-007) ou personifica com registro (RF-CORE-014); em troca, nenhuma conta de operação do dia guarda acesso permanente a dado de saúde, que é o menor privilégio que a decisão buscou. Continua podendo tudo o mais, inclusive atravessar o isolamento sobre o dado não clínico, sempre com registro de auditoria. Perda técnica que vem junto: toda comparação por igualdade com o literal de papel de administrador deixa de ser suficiente, e a que sobrar sem revisão vira negação indevida ao Super Administrador ou, pior, permissão indevida ao Administrador sobre dado de saúde, que é exatamente o que o placar de três classes do Como verificar mede.
Nota de fechamento [2026-09-15]: merge 9aa10db na main, gates plan, db, qa e review verdes (.claude/gates/{plan,db,qa,review}/modularfit-AUD-04.gate), deploy em produção pela sequência código antes da migração (imagem 9dccd19 no ar com o papel antigo, login provado, depois 0031_papel_super_administrador, 0032_escada_de_acesso e 0033_promocao_das_contas_de_topo, retrato super_administrador 3, aluno 1), versões em produção durante o card 9dccd19, 892ef33, final 9aa10db; última migração em produção passa de 0030 para 0033. Funções mfit.e_administracao() e mfit.e_super_administrador(), 37 sítios normativos classificados em SQL (35 reescritos pela 0032, 27 comuns e 8 privativos; 2 fora dela, 0002:17 estrutura e 0017:285 histórica, imutável), gatilho impedir_escalada_de_papel; ponto único papeis.ts no server e no web; 19 guardas de camada 1 reclassificadas (as 18 do gate mais web/src/fichas/autoria.ts:123, criada por {F3-09} depois do gate original); o administrador perde exatamente as 7 capacidades privativas de governança (atribuir plano e status, manter planos, bloquear, promover papel, personificar, excluir em definitivo, consultar a trilha) e mantém as demais capacidades comuns aos dois papéis, inclusive os textos da plataforma (decisão do Fiscal, caminho A do Drift 1); no total corrigido da Matriz de 47 linhas (Passo 2 do gate), o Administrador fecha em 20 e não em 33, porque a fronteira de dado clínico de 14 capacidades nunca foi dele, medida na mesma republicação do DRS e não retirada por este card. ops/semear-contas-de-topo.mjs com lista fechada de três e-mails, idempotente (3 inalteradas nas duas rodadas em produção); 4 scripts de ops passam a escolher o super. Placares H1: negativo 4 de 7, positivo 7 de 10 (os restantes nomeados por card dono). Testes: server 1010 com banco real, web 113. Achados do qa corrigidos no card: catálogo de planos mostrava ações privativas ao administrador; 35 asserções desatualizadas. Perda declarada: as 7 capacidades de governança do administrador, por ADR-017.
RF-CORE-014, RF-CORE-010, D.2, D.5 e a entidade sessao_personificacao de E.1. O Super Administrador assume a visão de um usuário para dar suporte, e tudo o que acontece nesse período fica rastreável até ele.
O que implementar: depende de {AUD-04}. (1) tabela sessao_personificacao, apenas acrescida, com personificador, personificado, início, fim e motivo do fim; (2) início da personificação por ação explícita com confirmação nominal a partir de /admin/usuarios, negada a todo papel que não seja Super Administrador e negada também quando o alvo é Super Administrador; (3) sessão personificada que carrega as permissões do personificado, nunca as do personificador, com as políticas de acesso por linha decidindo pelo personificado; (4) faixa permanente no topo de toda tela enquanto a personificação durar, nomeando quem está sendo personificado, mostrando o tempo restante e oferecendo a saída; (5) trilha de auditoria com entrada de início e entrada de fim, e com o par personificador e personificado em toda entrada gerada no período; (6) saída por ação explícita ou por expiração de trinta minutos, parâmetro de configuração, voltando à sessão do Super Administrador sem exigir nova entrada. (7) fechamento da personificação expirada em dois níveis: preguiçoso na aplicação, nos três pontos que já abrem transação, a passagem pela camada única de sessão, a renovação e o início de uma personificação nova, cobrindo todo operador que volta; mais uma rotina de operação chamada pelo agendador do sistema que já existe, de dez em dez minutos, cobrindo quem não volta, com a janela de até dez minutos declarada na ficha {ART-WF-19} do Inventário de Artefatos. Nenhum agendador de aplicação nasce aqui. (8) personificar conta bloqueada pela administração é permitido, porque personificar não é entrar e o bloqueio continua valendo para o próprio usuário: a única negação de alvo continua sendo a conta de Super Administrador.
Como verificar (teste primeiro): personificar um aluno e conferir, pela rota real, que a leitura devolve os dados dele e não os de outro aluno; conferir na tela que a faixa aparece em cada uma das rotas alcançadas, contando telas visitadas contra telas com faixa e fechando o placar; escrever um registro sob personificação e conferir que o autor gravado é o personificado e que a entrada de auditoria traz o par; tentar escrever numa categoria sensível sem consentimento do titular e esperar bloqueio; sair e conferir a entrada de fim; deixar expirar e conferir a segunda forma de fim; tentar personificar com conta de Administrador, de profissional e de aluno, esperando negação nas três; tentar personificar uma conta de Super Administrador, esperando negação.
Aceite (H1): Dado o Super Administrador autenticado e um aluno alvo, Quando inicia a personificação, Então as telas respondem com os dados e as permissões do aluno, E a faixa aparece em todas as telas alcançadas, E a trilha ganha a entrada de início com personificador, personificado e momento, E toda entrada gerada no período traz o par, E a escrita em categoria não consentida pelo titular continua bloqueada, E a saída explicita e a expiração de trinta minutos produzem cada uma a entrada de fim, E a mesma ação pedida por Administrador, profissional ou aluno é negada nos três casos, E personificar uma conta de Super Administrador é negado.
Nota de fechamento [2026-09-29]: merge 3dabc30, https://fit.modulareasy.com, migrações 0034, 0035 e 0036, gates plan, qa (rodada 2, PASS COM RESSALVAS) e review (APPROVED) verdes. Provado em produção pelo qa, com curl de cookie real e navegador em dois temas e duas larguras: sair pela faixa devolve o Super Administrador a /admin sem tela de entrada (8 de 8 combinações), /admin/auditoria mostra início e fim de personificação com o par (Autor "operador como alvo"), a faixa aparece em 44 de 44 telas visitadas do aluno (11 telas em 4 combinações), a expiração fecha a linha com o motivo certo, e as negações dão 403 para Administrador, profissional e aluno, para alvo Super Administrador e para si mesmo. {ART-WF-19} instalado no crontab do host, de dez em dez minutos. Ressalvas nominais, nenhuma bloqueia o card: /admin/usuarios é mínima e o resto é de {F7-14}; as negações de senha e e-mail sob personificação seguem sem rota para provar (as rotas ainda não existem); o travessão em /plano e em células vazias de /admin/auditoria e a carga direta de /privacidade e /plano sem marcador de sessão são preexistentes e viram cards próprios.
Perda declarada: nada do que existe hoje deixa de funcionar, porque a capacidade é nova. O que se perde de propósito é a possibilidade de o operador da plataforma olhar a tela do usuário sem deixar rastro: a partir deste card, ver o sistema pelos olhos de alguém custa duas entradas na trilha de auditoria e uma faixa visível na tela. O Administrador não ganha essa capacidade, e o suporte que ele presta continua sendo pelo alcance cross-unidade de RF-CORE-007, que mostra o dado sem assumir a sessão.
Por que: ADR-012 e L.2 são lei do próprio DRS ("nenhum limite vive fixo no código", "ajustáveis pela administração sem publicação nova") e o sistema as viola em universo medido de 148 pontos, dos quais 101 deveriam ser editáveis. {ART-CFG-01} catalogou 32 parâmetros e sua tarefa é {AUD-06}; a semente do card é de 28 parâmetros (32 publicados menos 4 de forma tabular). Este card é a tarefa.
O que implementar: depende de {AUD-04} (a tela e a escrita são privativas do Super Administrador). (1) tabela mfit.parametro, uma linha por chave do catálogo, com tipo, faixa, descrição, valor e a trava de cardinalidade contra o catálogo publicado; (2) ponto único de leitura parametro(chave) no servidor, com cache invalidado na escrita, pelo qual TODO consumidor passa; (3) migração dos parâmetros classificados A e B com DEFAULT igual ao valor de hoje, sem mudança de comportamento; (4) tela /admin/parametros, escrita privativa do Super Administrador e leitura do Administrador, renderizada a partir do catálogo (tipo, faixa e descrição por parâmetro), com o valor recusado fora da faixa; (5) toda alteração auditada, com valor antigo e valor novo na trilha; (6) verificador novo "zero literal de negócio fora do ponto único", por LITERAL e não por nome, com lista nominal de exceções e decoy; (7) os parâmetros técnicos continuam nomeados, no ponto único, com editavel = false, e o verificador os conhece.
RF-PAIN-011, ADR-012, L.2.
Fechamento [2026-09-29]: entregue e em produção (merge 29380ae, https://fit.modulareasy.com, /api/estado na versão 29380ae, migrações 0037 e 0038, gates plan, qa (PASS COM RESSALVAS) e review (APPROVED na rodada 4) verdes). mfit.parametro nasce com 28 linhas, o ponto único parametro(chave) atende os consumidores migrados, e /admin/parametros está no ar, com escrita do Super Administrador e leitura do Administrador, valor recusado fora da faixa e cada alteração na trilha com valor anterior e valor novo. O verificador ops/verifica-parametros.mjs fecha em RESULTADO OK, com 79 de 101 literais de negócio restantes fora do ponto único (22 fechados mais 79 restantes igual a 101, universo de 148, 0 soltos), cada restante com dono nominal ({AUD-07} 9, {AUD-08} 17, consentimento_textos 1, cards de módulo 52). O número de partida do card, 100 editáveis, foi ratificado em 101 na construção. Perda declarada: nada de comportamento deixou de funcionar, porque a semente é o valor medido de hoje; o Administrador não ganhou escrita, e aluno e profissional não ganharam leitura da tela. Ressalvas nomeadas do qa (sem P0 nem P1 abertos ao fim): (a) o histórico imutável da trilha ainda exibe, na coluna do valor anterior, o jargão antigo do motivo técnico (ADR-013, {F2-08}, {F7-07}, Fase 8), fiel ao que foi gravado, aceito; (b) a mensagem de bloqueio de login sem acento é preexistente de {F1-03}, não causada por este card; (c) ops/verifica-parametros.mjs não roda dentro da imagem do servidor, porque lê a árvore de fonte, e roda na árvore do commit. Lacuna declarada: os consumidores sessao_, recuperacao_, vinculo e url_assinada foram lidos no código e não exercitados, e a forma texto não tem parâmetro editável. {TB-55} segue aberto, por ser Beta.
Por que: listas_fechadas é um dos 32 parâmetros de {ART-CFG-01} e hoje vive fora do ponto único. Seis listas fechadas existem hoje como enum no banco e constante no código (grupo muscular, dificuldade, categoria, objetivo, nível, equipamento do aluno, 52 itens); migram para a tabela editável única. Mais dois CHECK de teto de lista, nove pontos de literal no total, todos fora do ponto único que {AUD-06} construiu.
O que implementar: migrar as seis listas fechadas para uma tabela editável única (mfit.lista_fechada_item), no precedente de {F1-18} (a constante virou tabela mantendo a mesma forma de import); as constantes e os dois CHECK de teto de lista passam a ler dessa tabela por {ART-CFG-02}, o ponto único, que ganha listaFechada(nome). As quatro listas ainda sem módulo (tipo de refeição {F4-01}, tipo de exame {F5-04}, pontos de dobra {F5-02}, categoria de pose {F5-08}) nascem nessa tabela pelo card dono. Tela /admin/listas.
Como verificar (teste primeiro): ops/verifica-parametros.mjs reprova, antes deste card, os nove pontos (sete constantes mais dois CHECK) como literal fora do ponto único, com card dono {AUD-07}. Os dois CHECK de teto são derrubados por migração posterior (DROP CONSTRAINT), e o verificador deixa de contá-los porque reconhece restrição derrubada, com decoy que prova que CHECK vivo continua sendo acusado. Os nove saem dos restantes: oito fechados e um reclassificado como técnico; restantes 79 para 70, editáveis 101 para 100.
Nota (gate modularfit-AUD-07, Drift 2, 2026-09-29): a sétima constante é server/src/arquivos/tipoConteudo.ts, mapa de protocolo e não lista de negócio, e é reclassificada de B para C, com o motivo "mapa extensão para tipo MIME (protocolo IANA); serve também os ativos da SPA; a regra de negócio é anexo_extensoes e midia_exercicio_formatos". O denominador de editáveis passa de 101 para 100.
Aceite (H1): Dado as seis listas fechadas hoje fixas em código, Quando o Super Administrador edita uma lista pela tela, Então o valor novo vale no próximo uso sem publicação nova, E os dois CHECK de teto são derrubados por migração posterior (DROP CONSTRAINT), E o verificador de {ART-CFG-02} deixa de contá-los porque reconhece restrição derrubada, com decoy que prova que CHECK vivo continua sendo acusado, E os nove saem dos restantes: oito fechados e um reclassificado como técnico.
RF-PAIN-011, ADR-012, L.2.
Fechamento [2026-09-29]: entregue e em produção (merge 7b47fa6, repositório wladimirdjr/modularfit, https://fit.modulareasy.com, /api/estado na versão 7b47fa6, migrações 0039 e 0040 aplicadas em dois deploys, com os seis tipos enum das listas removidos do banco; gates plan, qa (PASS COM RESSALVAS na rodada 1 e PASS depois da 0040, com os dois P2 corrigidos) e review (APPROVED na rodada 2) verdes). As seis listas fechadas vivem em mfit.lista_fechada_item (52 itens: 10, 3, 2, 8, 3 e 26), editadas em /admin/listas pelo Super Administrador, com leitura do Administrador, e listaFechada(nome) é a segunda leitura do ponto único. O verificador ops/verifica-parametros.mjs fecha em RESULTADO OK com 70 de 100 literais de negócio restantes (30 fechados, sendo 22 de {AUD-06} mais 8 de {AUD-07}; 70 restantes; 47 exceções C; universo 147): saíram dos restantes oito literais fechados e um reclassificado como técnico, e o denominador de editáveis passou de 101 para 100. Perda declarada: o filtro de busca deixa de oferecer item desativado, e criar exercício exige item ativo; o tipo fechado em TypeScript vira string, e a validação passa para o cache e o gatilho do banco; o catálogo deixa de ficar cinco minutos em cache no navegador; o Administrador continua sem escrita, e item nunca é apagado nem tem o valor trocado. Registro que já tinha um item desativado continua exibindo o rótulo e pode ser regravado. {TA-95} fecha; {TB-56} segue aberto, por ser Beta.
Por que: dezessete valores de negócio vivem como constante em config.ts fora do catálogo: senha mínima, limite de cadastro por endereço de origem (duas chaves), teto da biografia, remetente, onze assuntos de e-mail e a ajuda do campo de registro.
Pergunta que este card fecha, respondida: os onze assuntos entram em mfit.parametro, forma texto (Drift 1 do gate); o corpo dos e-mails continua no código do modelo até decisão do dono.
O que implementar: quinze chaves viram quatorze linhas de mfit.parametro (senha_minima_caracteres, cadastro_por_origem_maximo, perfil_biografia_max_caracteres e onze email_assunto_<modelo>), lidas pelo ponto único; remetente e ajuda de registro ficam como exceção técnica nomeada (L.3 e ADR-004); a cópia da biografia no web e as quatro cópias da senha mínima no web passam a ler o valor servido; /admin/parametros passa a editar a forma texto.
Como verificar (teste primeiro): o verificador reprova antes os dezessete com dono {AUD-08}; depois, quinze saem fechados, dois aparecem como exceção com motivo, web.Perfil.biografia sai fechado, e o placar honesto fecha em 52 de 98 (48 C, universo 146).
Aceite (H1): Dado os dezessete valores hoje soltos em código, Quando este card fecha, Então cada um tem destino declarado (mfit.parametro ou exceção técnica nomeada), E nenhum permanece literal sem classificação, E o verificador de {ART-CFG-02} imprime zero pendente com dono {AUD-08}, E nenhuma cópia do valor de senha mínima ou de teto de biografia sobrevive no web.
Nota (gate {AUD-08}, 2026-09-29): duas reclassificações de A para C, no molde do tipoConteudo de {AUD-07}. config.emailRemetente: marca única (L.3) e endereço autenticado no provedor de envio (SMTP_URL, SPF e DKIM do domínio): trocar o endereço exige infraestrutura, trocar o nome contraria L.3; {ART-MSG-01} já o fixa como "endereço institucional do produto". config.ajudaRegistroProfissional: reafirma ADR-004 ao usuário; o texto só muda quando a decisão muda, e editar sem deploy deixaria a tela afirmar o que o sistema não faz. Denominador de editáveis: 100 para 98.
RF-PAIN-011, ADR-012, L.2.
Fechamento [2026-09-29]: entregue e em produção (merge fa725bc, repositório wladimirdjr/modularfit, https://fit.modulareasy.com, /api/estado na versão fa725bc, migração 0041 aplicada, gates plan, qa (PASS COM RESSALVAS, zero P0 e zero P1) e review (APPROVED) verdes). mfit.parametro passa de 28 para 42 linhas (as catorze novas: senha_minima_caracteres, cadastro_por_origem_maximo, perfil_biografia_max_caracteres e onze email_assunto_<modelo>), server/src/config.ts passa de 26 para 11 chaves, e /admin/parametros edita a forma texto (uma linha, de 3 a 120 caracteres). O verificador ops/verifica-parametros.mjs fecha em RESULTADO OK com 52 de 98 literais de negócio restantes (fechados 46, sendo 22 de {AUD-06}, 8 de {AUD-07} e 16 de {AUD-08}; 48 exceções C; universo 146, 52 + 46 + 48; 0 soltas): quinze chaves fechadas, mais web.Perfil.biografia, e duas exceções nomeadas (remetente, por L.3, e ajuda do registro, por ADR-004). Provado em produção pelo qa: senha mínima de 8 para 10 (cadastro de 9 recusado com "10" na mensagem, dica em 10 sem reiniciar, redefinição idem), biografia de 1000 para 200, anti-abuso de 10 em 60 para 2 em 15 (terceiro cadastro da origem recebe 429), assunto de recuperação novo gravado no e-mail enviado, forma texto recusando vazio, espaços, 121 caracteres, quebra de linha e TAB, trilha com antes e depois em cada alteração e na restauração, negações 403 para Administrador, Profissional e Aluno, e o caminho antigo (cadastro de aluno e de profissional com a ajuda do registro, redefinição ponta a ponta, convite de vínculo) intacto. Perda declarada: os quinze valores deixam de ser conhecidos em tempo de compilação (quem garante passa a ser a faixa na escrita e a trava de cardinalidade na carga); a dica de tamanho da senha só mostra o número depois de uma requisição pequena e as telas de recuperação e redefinição perdem o cache de cinco minutos (uma busca por abertura); se o teto da biografia baixar, a biografia já gravada continua exibida inteira e editá-la exige encurtar até o teto (editar só o nome não toca a biografia); o assunto do e-mail pode divergir do corpo por decisão do operador; o remetente e a ajuda do registro continuam não editáveis. Ressalvas do qa, nenhuma do diff do card: (1) Perfil.tsx linha 913 com "verificação" e "administração" sem acento, e o verificador de ortografia não pega; (2) o cabeçalho do Perfil não atualiza o nome depois de salvar; (3) a recusa numérica devolve 400 e a de texto 422 na mesma rota, consistência a decidir; (4) e-mails de teste saíram de verdade pelo envio ativo, para endereços de teste da própria casa, já apagados; (5) um arquivo de saída de outra sessão existe na máquina de produção e não é do qa. Lacunas declaradas: a comparação com o código pai foi por leitura e não por execução (o teste não existe lá, e reprova por construção); o consumidor do envio de mídia de {TA-93} não foi exercitado; a negação foi provada na rota e a camada de segurança por linha do banco não foi testada separadamente. Dados de teste apagados, com o retrato dos 42 parâmetros idêntico ao inicial. {TA-96} fecha; {TB-55} segue aberto, por ser Beta.
Por que: a Matriz Ator por Capacidade do DRS dá N a Médico e Nutricionista em "Criar ficha de treino", "Duplicar ficha de terceiro" e "Atender ou recusar solicitação de ficha" (RF-TREI-001, RF-TREI-004, RF-TREI-009; {ART-ACS-03} e {ART-ACS-05}: "Não cria ficha de treino"), e em produção os dois criam ficha hoje (medido no gate de {F4-02}, M-3, em 29/09/2026). Achado do loop, triado pela Inspeção.
Depende de: {F4-02}, que cria o primeiro auxiliar de capacidade por especialidade no banco (D-3 do gate de {F4-02}); este card o reusa, nunca cria um segundo.
O que implementar: aplicar o auxiliar de especialidade nas políticas de escrita de ficha_treino (criação e duplicação) e de solicitacao_ficha (atender e recusar), e a mesma guarda na rota com 403 nomeado; verificador que confere as três capacidades contra a Matriz, com decoy e controle positivo (Personal Trainer e Fisioterapeuta continuam criando; Aluno continua criando para si).
Como verificar (teste primeiro): o verificador reprova antes do card (Médico e Nutricionista criam, duplicam e atendem hoje) e aprova depois, com decoy (política que libera Médico ou Nutricionista reprova) e controle positivo (Personal Trainer e Fisioterapeuta continuam criando; Aluno cria para si).
Aceite (H1): Dado um Médico e um Nutricionista com vínculo ativo com o mesmo aluno, Quando cada um tenta criar ficha, duplicar a ficha do Personal e atender uma solicitação desse aluno pela tela e pela rota direta, Então as seis tentativas recebem 403 nomeado sem linha gravada, E o Personal Trainer e o Fisioterapeuta com vínculo ativo fazem as três com sucesso, E o aluno cria ficha para si.
Perda declarada: Médico e Nutricionista deixam de criar, duplicar e atender ficha de treino; ficha que um deles já criou continua existindo e legível pelas regras de sempre (não se apaga dado). O aluno deixa de poder pedir ficha ou ajuste a Médico e Nutricionista.
Nota (Drift 1, gate modularfit-AUD-09, 2026-09-30): o escopo deste card inclui editar, desativar e reativar ficha de que é autor (linha Editar da Matriz, N para Médico e Nutricionista); a ficha antiga de um deles só é alterada pela administração (RN-009).
Nota (Drift 2, gate modularfit-AUD-09, 2026-09-30): POST /api/solicitacoes recusa com 403 nomeado quando o destinatário não prescreve treino; a tela só oferece destinatários que prescrevem e não oferece "Pedir ajuste" em ficha cujo autor não prescreve. Sem isso o pedido ficaria aberto para sempre, o caminho morto que a própria Matriz proíbe. Como o Aceite (H1) fala em atender uma solicitação desse aluno, depois do card um pedido a Médico ou Nutricionista só existe como dado antigo, e o teste {TA-97} prova as duas pontas: o aluno não consegue pedir, e o pedido antigo, semeado pelo arnês, não é atendido nem recusado.
Nota (gate {AUD-09}, Drift 4, 2026-09-30, zero medido): contagem em produção por SELECT em transação somente leitura: 0 fichas com autor Médico ou Nutricionista e 0 pedidos de ficha ou ajuste dirigidos a Médico ou Nutricionista, abertos ou encerrados. Nenhum dado antigo muda de destino; a perda declarada vale só daqui para a frente.
Ordem: entra na fila depois de {F4-02} (dependência) e antes de {F4-03}.
Fechamento [2026-09-30]: entregue e em produção (merge d760b2d, repositório wladimirdjr/modularfit, https://fit.modulareasy.com, migração 0045, gates plan (APPROVED), db (PASS), qa (PASS COM RESSALVAS, zero P0 e zero P1) e review (APPROVED na rodada 2) verdes). A migração altera seis políticas (ficha_treino na criação e na atualização, ficha_item na inserção, na atualização e na exclusão, e solicitacao_ficha na atualização) para só deixar escrever quem é aluno para si, Personal Trainer ou Fisioterapeuta com vínculo ativo, reusando mfit.tem_vinculo_ativo_na_especialidade de {F4-02} (nenhum segundo auxiliar), e a rota recusa com 403 nomeado ("A sua especialidade não prescreve ficha de treino"). Contagem do Drift 4 em produção: zero fichas e zero pedidos de Médico ou Nutricionista, então nenhum dado antigo mudou de destino. Verificador de capacidade por especialidade: de 1 de 7 para 7 de 7, com zero políticas chamando o auxiliar sem estar declaradas. Provado em produção pelo qa: Médico e Nutricionista 0 de 7 ações (criar, duplicar, editar, desativar, reativar, atender, recusar), 14 de 14 negações com 403 nomeado e banco idêntico antes e depois, cada uma com o seu par positivo (Personal Trainer e Fisioterapeuta 7 de 7, aluno cria, duplica e edita a própria ficha, Administrador edita a ficha antiga, personificação do Super Administrador preservada), leitura de Médico e Nutricionista intacta, e smoke visual nos dois temas em 400 e 1280 pixels sem travessão e sem erro de console. Perda declarada: Médico e Nutricionista deixam de criar, duplicar, editar, desativar, reativar, atender e recusar ficha de treino (leitura preservada); o aluno deixa de poder pedir ficha ou ajuste a eles; a ficha que um deles já tivesse criado continua legível e só a administração a altera (RN-009). Ressalvas do qa, os quatro P2, corrigidos em 26872cb, já dentro do merge: painel de Médico e Nutricionista com pedidos antigos sem ação nem explicação, vazio do aluno que só tem Médico contradizendo o aviso, concordância de "Pedido de ajuste aberta" e quebra do "+ Adicionar" a 1280 pixels no catálogo. Lacuna declarada: o baseline "o teste reprova sem o fix" não foi rodado em produção (a 0045 não se reverte lá); a prova é o arnês contra o código pai. Dados de teste apagados, zero resíduo.
RF-TREI-001, RF-TREI-004, RF-TREI-009, Matriz Ator por Capacidade; teste {TA-97}.
Por que: RF-CORE-010 e RN-001 fechavam as categorias de consentimento em seis (exame, prescrição, anamnese, avaliação física, sessão de reabilitação e foto de progresso), e a mesma especificação classifica nutrição como dado clínico na fronteira da administração (B.1 e RF-CORE-007: "dez por classificação, que são as categorias de saúde, nutrição e corpo"); a lei de proteção de dados trata o dado referente à saúde como sensível. Medido pelo loop em 30/09/2026: refeicao e meta_nutricional estão com sensivel: false e categoriaConsentimento: null no registro de entidades (server/src/acesso/verificarPoliticas.ts, linhas 146 a 148, junto de plano_alimentar, que ainda não existe), e um Personal Trainer vinculado a um aluno sem consentimento nenhum lê as duas com 200. Decisão de escopo da Inspeção.
Decisão, já tomada: nutrição é a sétima categoria, cobrindo refeição (com os alimentos dela), meta nutricional e plano alimentar (com as refeições do plano), com finalidade e texto próprios e restrição por especialidade disponível como nas outras seis (RN-003). RF-CORE-010 (Notas e H1), RN-001, E.1, J.2 e o módulo diet (C.9) do DRS estão alinhados nesta data.
O que implementar: o valor nutricao no tipo mfit.categoria_sensivel; refeicao, meta_nutricional e plano_alimentar com sensivel: true e categoriaConsentimento: 'nutricao' no registro de entidades, refeicao_alimento e plano_refeicao seguindo a linha pai; a quinta seção do gabarito (GABARITO-ACESSO-ALUNO.md, "Consentimento por categoria") aplicada às duas tabelas que existem, com a política de leitura em conjunção com mfit.consentimento_visivel(aluno_id, 'nutricao') e o WITH CHECK de escrita com mfit.tem_consentimento(aluno_id, 'nutricao'), e plano_alimentar nascendo já assim em {F4-03}; as rotas de refeição e de meta traduzindo a recusa em apresentação da tela de consentimento da categoria, pelo mesmo tradutor das outras seis; /privacidade listando sete categorias; a vigência padrão da meta (semearMetaPadrao) saindo da criação do aluno para o primeiro consentimento de nutrição concedido, lendo metas_padrao naquele momento; e o sétimo texto de consentimento.
Texto do consentimento, dono é este card: docs/textos/consentimentos.md tem os seis textos, cada um em três blocos (o que é coletado, quem vê, revogação). Este card escreve o sétimo, Nutrição, no mesmo molde (o que é coletado: refeições com alimentos e quantidades, metas diárias de calorias e macronutrientes e o plano alimentar prescrito; quem vê: o aluno e o profissional com vínculo ativo, restringível a especialidade na tela de privacidade, e a administração nos termos das outras seis; revogação: bloqueia registro novo e retira a vista dos profissionais, sem apagar o que já existe), corrige a abertura do arquivo de "seis categorias" para "sete categorias" e carrega o sétimo texto, versão 1, onde os seis vivem hoje (consentimento_textos, {ART-CFG-01}).
Dado já existente: refeição e meta gravadas antes do consentimento permanecem, legíveis pelo titular e pelo Super Administrador, fora da vista do profissional e sem escrita nova até o consentimento, a mesma situação de uma categoria revogada. A vigência padrão de aluno sem consentimento de nutrição, por ser cópia de metas_padrao e não dado da pessoa, é removida pela migração e volta a nascer no consentimento. Nenhuma refeição e nenhuma meta escrita por pessoa é apagada.
Contagem: universo das categorias medido por outro meio que o texto: os valores de mfit.categoria_sensivel em produção, lidos em transação somente leitura em 30/09/2026 contra o HEAD b569460: 6 (exame, prescricao, anamnese, avaliacao_fisica, sessao_reabilitacao, foto_progresso); depois do card, 7. Entidades de nutrição em E.1: 5 tabelas, das quais 3 decidem acesso (refeicao, meta_nutricional, plano_alimentar) e 2 seguem a linha pai; em produção existem 3 das 5 (refeicao, refeicao_alimento, meta_nutricional): 3 existentes mais 2 a nascer em {F4-03} igual 5. Dado existente, mesma leitura: 0 refeições; 1 vigência de meta, a padrão (autor nulo), de 1 aluno, que é o único aluno da base, por isso nada aqui se afirma de "todos": o gate do card mede de novo antes da migração. Critério comportamental: leitura real pela rota com sessão de Personal Trainer vinculado, sem consentimento (negada) e com consentimento (servida), nunca busca por nome de categoria no código. RF que reprova: RF-CORE-010 (H1 com a cláusula de nutrição) e D.6.
Como verificar (teste primeiro): antes do card, o arnês com banco real prova o defeito: aluno sem consentimento, o Personal Trainer vinculado lê refeição e meta com 200 e o aluno grava refeição; o verificador de políticas reprova refeicao e meta_nutricional sem a categoria. Depois, as mesmas chamadas invertem, com decoy (política de leitura sem consentimento_visivel reprova) e controle positivo (com consentimento, Personal Trainer e Nutricionista leem; com a categoria restrita a Nutricionista, só o Nutricionista lê).
Aceite (H1): Dado um aluno sem consentimento da categoria nutrição e um Personal Trainer com vínculo ativo com ele, Quando o Personal pede a refeição, a meta nutricional e o plano alimentar do aluno pela tela e pela rota direta, Então as leituras são negadas sem conteúdo parcial, E a primeira refeição que o aluno tenta registrar é bloqueada sem linha gravada e a tela de consentimento de nutrição é apresentada, E depois de conceder a mesma refeição grava, a vigência padrão da meta nasce e o Personal passa a ler as duas, E com a categoria restrita a Nutricionista o Personal volta a ser negado e o Nutricionista lê, E depois da revogação uma refeição nova é bloqueada enquanto as já registradas continuam legíveis pelo aluno.
Perda declarada: o aluno passa a consentir antes do primeiro registro de refeição ou de meta; o profissional vinculado sem o consentimento do aluno deixa de ler nutrição, inclusive o Personal Trainer, que hoje lê; a meta padrão do aluno passa a ser a vigente no dia do consentimento, não a do dia do cadastro; o aluno que nunca consentir não tem meta.
Ordem: entra na fila depois de {AUD-09} e antes de {AUD-10} e de {F4-03}.
Nota (gate {AUD-11}, drifts 1, 2, 3, 5 e 7, 2026-09-30): (1) marcar as entidades de nutrição como sensíveis exige o campo leitorUnico no registro de entidades e o critério (a2) do verificador do leitor único, conforme a Nota de {ART-AUD-02} do Inventário. (2) O card entrega duas migrações: 0046_categoria_nutricao.sql, somente com ALTER TYPE mfit.categoria_sensivel ADD VALUE 'nutricao', e 0047_consentimento_nutricao.sql, com todo o resto (as políticas citam 'nutricao'), porque o Postgres não deixa usar o valor novo de um enum na mesma transação em que ele nasce e o executor de migrações roda cada arquivo em BEGIN/COMMIT. {F4-03} passa a usar a migração 0048. (3) Revogar o consentimento bloqueia também a desativação de refeição antiga, porque o WITH CHECK de toda política de UPDATE de tabela sensível exige mfit.tem_consentimento: o aluno que revogou reconsente para desativar (decidido, ajuste se discordar). (5) Os textos de consentimento vivem em config.consentimentoCategorias (server/src/config.ts, versão 1.3), não na tabela consentimento_textos: o sétimo texto entra ali e em docs/textos/consentimentos.md, e a leitura "onde os seis vivem hoje (consentimento_textos)" do parágrafo acima se lê com esta correção; Termos de Uso e política de privacidade seguem fora do card. (7) A inserção de meta_nutricional exige também mfit.consentimento_visivel(aluno_id, 'nutricao'), conforme a Nota de RF-CORE-010 no DRS: o Nutricionista fora da restrição escolhida pelo aluno não define a meta.
Fechamento [2026-09-30]: entregue e em produção (merge 6490072, repositório wladimirdjr/modularfit, https://fit.modulareasy.com, /api/estado na versão servida 6490072 (= merge), migrações 0046 e 0047, gates plan (APPROVED com trava de drift), db (PASS), qa (PASS, {TA-98} 61 de 61, zero P0 e zero P1, dois P2 corrigidos dentro do merge) e review (APPROVED, 0 Critical, 0 Important, 7 Minor) verdes). Nutrição é a sétima categoria de consentimento: o tipo mfit.categoria_sensivel passa de 6 para 7 valores (0046, só o ADD VALUE) e sete políticas (refeicao na leitura, na inserção e na atualização, refeicao_alimento na leitura e na inserção, meta_nutricional na leitura e na inserção) passam a exigir mfit.consentimento_visivel na leitura e mfit.tem_consentimento na escrita (0047, por ALTER POLICY, sem apagar política). Placares medidos no fechamento pelo loop, nunca projetados: leitor único 2 de 10 (refeicao e meta_nutricional, cada uma no ponto único do seu módulo, declarado em leitorUnico); consentimento por entidade 2 de 10 (8 pendentes: plano_alimentar, medida_corporal e as seis de saúde); critério (h) 7 = 7; fronteira clínica 2 de 13 (13 no registro, inalterado); verifica-parametros sem mudar (52 de 98, C 53, universo 151). H1 provado em produção ({TA-98}): Personal Trainer, Médico e Nutricionista vinculados, sem consentimento, recebem 403 categoria_nao_compartilhada em refeição e em meta, sem conteúdo parcial e sem 200 vazio; o aluno sem consentimento vê o cartão de consentimento no lugar do formulário, e a gravação direta volta 403 consentimento_necessario sem linha gravada; ao consentir, a meta padrão nasce uma vez (reconceder dá 409 e não duplica) e a refeição grava; com a categoria restrita a Nutricionista só ele lê e grava, e restrita a Médico o Nutricionista fica de fora também da escrita da meta; revogado, os profissionais voltam a 403, o titular segue lendo o histórico e desativar refeição antiga é bloqueado até reconsentir; Administrador com 403 nomeado e 0 linhas no banco (controle do aluno 1|3|1); Super lê e grava a trilha (+4 entradas) e, personificando, grava com consentimento e é negado sem. Dados de teste apagados, zero resíduo. Perda declarada, confirmada em produção: o profissional vinculado sem consentimento de nutrição, ou fora da restrição, deixa de ler refeição e meta, inclusive o Personal Trainer, que lia com 200; o Nutricionista fora da restrição deixa de gravar meta; o aluno sem consentimento deixa de registrar refeição, de definir meta e de desativar refeição antiga; a meta padrão passa a nascer no primeiro consentimento e não no cadastro, e a vigência padrão de aluno sem consentimento foi removida pela migração (era cópia de metas_padrao, conforme o gate de db); nenhuma refeição e nenhuma meta escrita por pessoa foi apagada. Quem continua: o titular lê tudo sempre, o Super lê com auditoria, o Administrador segue negado, as seis categorias de saúde e /privacidade (agora com sete cartões) seguem iguais. Não entra aqui: os textos de consentimento editáveis e versionados são de {AUD-12}, e plano_alimentar nasce já sob a categoria em {F4-03} (migração 0048).
RF-CORE-010, RN-001, RN-003, E.1, D.6, RF-DIET-001 a RF-DIET-005; teste {TA-98}.
Por que: ordem do dono de 15/09/2026: "Todas as regras de negócio do sistema precisam ser configuráveis pelo painel de super admin. Coisas como planos, precos, limites, e etc." ADR-012 já trata textos como dado de configuração. Hoje o assunto dos onze e-mails transacionais é editável ({AUD-08}, em mfit.parametro) e o corpo vive em código (server/src/email/modelos.ts), fora de qualquer tela. O gate de {AUD-08} (Drift 9) desenhou este caminho como Cenário 2: forma tabular, tabela própria (D-15 de {AUD-06}), e as onze linhas email_assunto_<modelo> migram para ela. Decisão de escopo da Inspeção.
Decisão, já tomada: extensão de RF-PAIN-011 (os modelos são um parâmetro de forma tabular do catálogo, como listas_fechadas), sem RF novo e sem capacidade privativa nova em B.1, com tela própria /admin/emails. RF-PAIN-011, ADR-012, L.2 e o Apêndice de catálogo de telas do DRS estão alinhados nesta data.
O que implementar: a tabela do modelo (um registro por modelo: chave, evento, variáveis permitidas, variáveis obrigatórias, versão vigente) e a tabela das versões, só de acréscimo (modelo, número, assunto, corpo, autor, criada em), com a semente da versão 1 de cada modelo igual ao texto de hoje (assunto = valor vigente do parâmetro em produção no dia da migração; corpo = o texto de hoje do código, com as variáveis no lugar das interpolações); as onze linhas email_assunto_<modelo> saem de mfit.parametro (trava de cardinalidade de 44 para 33, medido hoje; o gate mede de novo); o renderizador único lê a versão vigente, substitui as variáveis, deriva o HTML (parágrafos, valor escapado, variável de endereço como link) e grava no registro do envio o número da versão usada; modelos.ts deixa de ter texto de e-mail; rotas de leitura (dois papéis de administração), de gravação e de restauração (só Super Administrador) e de pré-visualização (sem gravar e sem enviar); tela /admin/emails com a lista dos onze modelos, o editor de assunto e corpo com as variáveis do modelo à vista, a prévia, o histórico de versões com restaurar e a confirmação nominal ao gravar. Ficha, regras de validação e catálogo de variáveis no cartão {ART-CFG-03} do Inventário.
Contagem: universo medido por três meios, em 30/09/2026, contra o HEAD b569460: 11 funções de modelo exportadas em server/src/email/modelos.ts; 11 entradas no mapa de modelos do envio (server/src/email/rotina.ts); 11 linhas email_assunto_% em mfit.parametro, lidas em transação somente leitura. 11 = 11 = 11. Lado negativo, fora do universo por decisão escrita: o remetente (config.emailRemetente, exceção técnica de {AUD-08} por L.3) e a ajuda do registro profissional (ADR-004), que não são modelo. Critério comportamental: modelo que o envio alcança pelo mapa de modelos e renderiza, não busca por nome. Produto de uma instância só, sem inquilinos: o universo é o sistema inteiro. RF que reprova: RF-PAIN-011, H1 dos modelos de e-mail.
Como verificar (teste primeiro): o verificador de parâmetros ({ART-CFG-02}) ganha o critério "texto de e-mail em código" e reprova antes (onze corpos literais em modelos.ts) e aprova depois (zero), com decoy (um corpo literal reintroduzido reprova) e controle negativo (o remetente, exceção nomeada, não reprova); o arnês prova a gravação válida, as três recusas (variável desconhecida, variável de endereço ausente, assunto com quebra de linha), a versão nova por gravação, a restauração como versão nova, a prévia sem linha gravada e sem e-mail na fila, a prévia dos onze modelos na versão da semente idêntica ao texto que o código produzia para os mesmos dados, e o e-mail seguinte saindo com o texto novo.
Aceite (H1): Dado o modelo de recuperação de senha na versão vigente N, Quando o Super Administrador grava em /admin/emails um assunto e um corpo novos que usam apenas variáveis da lista do modelo, Então a versão N+1 passa a ser a vigente e a trilha registra antes e depois, E o próximo e-mail de recuperação renderizado sai com o assunto e o corpo novos e as variáveis substituídas, sem publicação nova e sem reiniciar o serviço, E um corpo com variável fora da lista, ou sem a variável de endereço, é recusado com a variável nomeada na mensagem e a vigente continua N+1, E a pré-visualização devolve o texto renderizado sem gravar versão e sem enviar e-mail, E a mesma gravação tentada pelo Administrador é negada pela rota direta, E nenhum corpo de e-mail transacional permanece como texto literal no código.
Perda declarada: o assunto passa a morar junto do corpo, no modelo, e sai de /admin/parametros; as alterações de assunto já feitas em /admin/parametros continuam na trilha de auditoria e não viram versões do modelo (a versão 1 parte do valor vigente); os onze textos deixam de ser conhecidos em tempo de compilação, e texto errado passa a ser possível por decisão do operador, contido pela lista de variáveis, pela variável de endereço obrigatória e pela prévia; o operador não formata HTML.
Ordem: entra na fila depois de {AUD-11} e antes de {F4-03}.
Nota (Inspeção, gate {AUD-10}, drifts 1 a 4, 6 a 8 e 10, 2026-09-30): o gate do planner corrige e acrescenta ao card, sem marcar entrega. (1) A migração deste card é a 0048 (uma só, 0048_modelos_de_email.sql); a Nota do gate de {AUD-11} e a Nota de {F4-03}, que dizem que {F4-03} passa a usar a 0048, estão superadas: {AUD-12} numera a seguinte (0049, ou 0049 e 0050 se precisar de ADD VALUE em chave_texto ou ponto_texto) e {F4-03} vem depois dela. (2) A versão vigente não é coluna da tabela do modelo: é derivada, o maior número de versão do modelo; a tabela do modelo ganha rotulo, faixa (as faixas do assunto e do corpo como dado) e parametro_legado_id, com a finalidade descrita em {ART-CFG-03}. (3) {{endereco}} é recusada no assunto, porque carrega o endereço de uso único, e continua obrigatória no corpo. (4) A trilha das alterações antigas de assunto continua legível em /admin/auditoria pelo parametro_legado_id; elas não viram versões. (5) A pré-visualização atende os dois papéis, e o Administrador vê o renderizado das versões gravadas, sem editor. (6) Os corpos saem para a semente da migração e o verificador de ortografia passa a varrê-la. (7) Texto e HTML de dois modelos já divergiam no código (recuperacao_senha e acesso_criado: "Acesse" no texto, "Clique" no HTML); a semente parte do texto e o HTML desses dois passa a dizer "Acesse". (8) Perda declarada adicional: a versão é imutável, então a prova em produção deixa rastro, e recuperacao_senha fica com três versões depois do qa (a versão 2, de teste, e a versão 3, restauração da versão 1), como descrito na Nota de {TA-99}.
Fechamento [2026-09-30]: entregue e em produção (merge 17b62d6, migrações 0048 e 0049, https://fit.modulareasy.com, /api/estado na versão 17b62d6; gates plan, db, qa (PASS COM RESSALVAS, zero P0 e zero P1) e review (APPROVED) verdes). Os onze e-mails transacionais têm modelo editável pelo Super Administrador em /admin/emails, com assunto e corpo, variáveis validadas no servidor, versões só de acréscimo, restauração como versão nova e pré-visualização para os dois papéis; o Administrador lê e não grava. mfit.parametro passou de 44 para 33 linhas e nenhuma email_assunto_ sobrou; a trava de cardinalidade é 33. Texto de e-mail em código: 11 antes, 0 depois, com decoy que reprova e controle que não. Placar numérico do verificador de parâmetros inalterado, 52 de 98, C 53, universo 151, porque o card move texto e não literal numérico. Em {TA-99}: 12 de 12 critérios e 38 de 38 na bateria de rotas, mais a tela nos dois temas e nas duas larguras; a regressão de {TA-96} (os e-mails reais de cadastro, convite, aceite, pedido de ficha, encerramento, cancelamento, recusa e recuperação) passou, 14 de 14 idênticos ao texto de sempre. Correção medida que vale sobre o que o gate dizia: são 9 modelos idênticos e 2 com a troca declarada de "Clique" para "Acesse" no HTML (recuperacao_senha e acesso_criado), e não 8 e 3. A migração 0049 só tirou o código interno de card da descrição do evento acesso_criado, que aparecia para o usuário na tela (P2-1 do qa, corrigido). Perda declarada, confirmada: o assunto saiu de /admin/parametros; e-mail já renderizado e na fila sai com o texto de quando foi montado; o HTML dos dois modelos acima diz "Acesse"; a versão é imutável, então a prova em produção deixou rastro: recuperacao_senha tem a versão 1, a versão 2 (de teste, com o marcador no assunto e no corpo) e a versão 3, que é a restauração da versão 1 e a vigente, 13 versões no total. Ressalvas, nominais: (P2-2) a tela só recusa um texto inválido depois de o Super confirmar a gravação, e não antes, não corrigido; acesso_criado e solicitacao_ficha_atendida não foram disparados em produção, cobertos pela semente e pelo arnês; a política de inserção do banco não foi testada isolada, só pelo 403 do tratador; restaurar pela tela não foi confirmado com sucesso em produção (para não criar uma versão 4), a confirmação e o 409 foram vistos na tela e o 201 da restauração veio pela rota; a trilha antiga de assunto (Drift 3) não foi conferida em produção. Próximo da fila: {AUD-12}, depois {F4-03}.
RF-PAIN-011, ADR-012, L.2, {ART-CFG-01}, {ART-CFG-03}, {ART-MSG-01} a {ART-MSG-03}; teste {TA-99}.
Por que: ordem do dono de 15/09/2026, literal: "Todas as regras de negócio do sistema precisam ser configuráveis pelo painel de super admin. Coisas como planos, precos, limites, e etc." ADR-012 trata texto como carga de configuração. Medido na revisão de {AUD-11} (30/09/2026, M6): os textos das sete categorias de consentimento vivem em server/src/config.ts com versão fixa no código ("1.3" nas seis do HEAD d760b2d, "1" no texto de nutrição de {AUD-11}), e consentimento_textos em {ART-CFG-01} está sem consumidor e com dono a confirmar. E (M7): restrito_a_especialidades volta do driver como texto e a tela testa com includes sobre a string, o que acende o chip errado quando uma especialidade tiver prefixo de outra. Decisão de escopo da Inspeção.
O que implementar: os sete textos passam a viver no mecanismo de texto versionado que já existe desde {F1-18} (mfit.texto_versionado, tela /admin/textos, só acrescido, vigência por publicado_em), um ponto por categoria; a concessão grava em consentimento.versao_texto a versão EXIBIDA ao aluno (409 texto_atualizado se mudou, como no cadastro); a semente carrega os textos de docs/textos/consentimentos.md; consentimento_textos em {ART-CFG-01} ganha dono {AUD-12} e deixa de ser "a confirmar"; a rota de consentimentos devolve restrito_a_especialidades como lista (cast ::text[] no SELECT, lição de {F3-08}) e a tela compara por item.
Contagem: universo dos textos = universo das categorias, medido por outro meio que o texto: os valores de mfit.categoria_sensivel (seis no HEAD d760b2d, sete quando {AUD-11} entregar) contra as chaves de config.consentimentoCategorias em server/src/config.ts (seis no HEAD, sete com {AUD-11}); depois do card, sete pontos de texto versionado, um por categoria, e zero textos de consentimento em constante de código. Critério comportamental: o texto que a rota de consentimentos entrega ao aluno vem da vigência de mfit.texto_versionado, e a versão gravada na concessão é a que a tela exibiu, nunca busca por nome de chave no código. RF que reprova: RF-CORE-010 e RF-CORE-012 (H1 abaixo).
Como verificar (teste primeiro): antes do card, o arnês com banco real prova o defeito: publicar uma versão nova do texto pela tela não muda o que a concessão exibe nem a versão que grava, e uma especialidade com prefixo de outra acende o chip errado. Depois, as mesmas ações invertem, com controle positivo (a concessão sem versão nova grava a versão exibida) e o caso da tela aberta com a versão anterior recebendo 409 texto_atualizado.
Aceite (H1): Dado o Super Administrador que publica em /admin/textos uma versão nova do texto de consentimento de nutrição, Quando um aluno abre a concessão dessa categoria, Então vê o texto novo sem publicação de código, E a concessão grava a versão exibida, E um aluno que tinha a tela aberta com a versão anterior recebe 409 texto_atualizado ao conceder, E um consentimento restrito a uma especialidade marca só o chip dela.
Perda declarada: nenhuma para o usuário; o texto passa a ser editado por tela, e a constante em config.ts morre.
Ordem: entra na fila depois de {AUD-10} e antes de {F4-03}.
Nota (Inspeção, gate {AUD-10}, drift 8, 2026-09-30): {AUD-10} usa a migração 0048; este card numera a seguinte: 0049, ou 0049 e 0050 se precisar de ADD VALUE em chave_texto ou ponto_texto. {F4-03} vem depois deste card.
Nota (Inspeção, gate {AUD-12}, drifts 1 a 4 e perda declarada, 2026-09-30): o gate do planner corrige e acrescenta ao card, sem marcar entrega. (1) Semente: onde "O que implementar" diz que a semente carrega os textos de docs/textos/consentimentos.md, leia: a semente carrega os textos e as finalidades carimbados nos consentimentos existentes, isto é, exatamente os de config.consentimentoCategorias (server/src/config.ts) no merge 17b62d6, nas versões 1.3 (seis categorias) e 1 (nutrição). O arquivo consentimentos.md nunca foi o texto exibido ao aluno e descreve quem vê o dado de forma diferente do que o sistema faz; não é fonte da semente. Se o dono quiser os textos longos, é publicação pela tela que este card entrega, depois de corrigidos, sem código. (2) Versão nova: vale para as próximas concessões; quem já consentiu guarda a versão aceita, continua com o consentimento válido e vê em /privacidade o texto dessa versão. Forçar novo consentimento a cada versão não é deste card; é card próprio se o dono pedir. (3) Quem publica: texto de consentimento cai na permissão que /admin/textos já tem, então o Administrador e o Super Administrador publicam, coerente com a decisão de 15/09 de manter "textos" com o Administrador; nenhuma capacidade nova nasce e a divergência de B.1 continua 22. (4) Migrações: duas, e não uma: 0050 (somente os nove ADD VALUE, dois em chave_texto e sete em ponto_texto) e 0051 (a carga dos textos e das finalidades, os comentários e as conferências com aborto). Isto supera a Nota anterior deste card, que previa a 0049; {F4-03} passa a usar a 0052. (5) Perda declarada (substitui "nenhuma para o usuário"): o aluno com a tela aberta quando sai versão nova recebe "texto atualizado" e precisa reler antes de conceder; o aluno com aba do front antigo no momento do deploy falha ao conceder, com erro genérico, até recarregar; quem já consentiu continua vendo o texto que aceitou, e só vê o novo se revogar e conceder de novo; a tela de privacidade depois de revogar passa a recarregar da API; a edição do texto de consentimento não é exclusiva do Super Administrador; o texto deixa de ser conhecido em tempo de compilação e de mudar por deploy, e texto errado passa a ser possível por decisão do operador, contido pela confirmação nominal e pela trilha; consentimentos.md deixa de ser tratado como fonte do texto exibido; e a prova em produção deixa dois registros permanentes no histórico de nutrição (uma versão de teste e a restauração com o texto da versão 1).
Fechamento [2026-09-30]: merge 4e5ed26 (repositório wladimirdjr/modularfit, https://fit.modulareasy.com, migrações 0050 e 0051; gates plan (APPROVED COM TRAVA DE DRIFT, travas cumpridas), db, qa (PASS COM RESSALVAS, {TA-100} 27 de 27, reconferência em produção na versão 53c7fd2 PASS) e review (APPROVED, Minors principais fechados em 53c7fd2, três declarados no CHANGELOG) verdes). Os sete textos de consentimento e as sete finalidades vivem em mfit.texto_versionado, editáveis pelo Super Administrador e pelo Administrador em /admin/textos (terceiro grupo, Consentimentos), só de acréscimo, com vigência por publicação; a constante config.consentimentoCategorias morreu. Placares medidos: texto de consentimento em código 7 antes, 0 depois, com decoy que reprova e controle que não; semente de 14 linhas (7 textos e 7 finalidades) com md5 igual ao texto carimbado nos consentimentos existentes; verifica-parametros de 52 de 98 para 51 de 98, C 54, universo 152; divergência de capacidades B.1 inalterada em 22. A concessão grava em consentimento.versao_texto a versão EXIBIDA: versão ausente ou divergente dá 409 texto_atualizado (com o vigente no corpo) e nenhuma linha gravada; segunda concessão da mesma categoria dá 409 ja_concedido e a tela converge para Concedido sem duplicata (reconferência, duas abas). Quem já consentiu segue com a versão aceita e vê o texto dela. restrito_a_especialidades chega como lista e a tela compara por item (só o chip Nutricionista acende; Personal Trainer 403 categoria_nao_compartilhada, Nutricionista 200). Perda declarada, confirmada: o aluno com a tela aberta quando sai versão nova recebe 409 e precisa reler; quem já consentiu só vê o texto novo se revogar e conceder de novo; o Administrador comum publica texto de consentimento; o texto errado passou a ser possível por decisão do operador, contido pela confirmação nominal e pela trilha; consentimentos.md deixou de ser fonte do texto exibido. Ressalvas, nominais: (P2-1) o cartão Termos de uso em /admin/textos mostra markdown cru, pré-existente de {F1-18}, fora deste card, não corrigido; (P2-2) a tela de consentimento não avisa ao aluno já consentido que há versão nova, decidido fora de escopo; a cláusula "especialidade com prefixo de outra" não tem caso real em produção (enum de quatro valores, nenhum prefixo de outro), provada pela causa-raiz (array JSON) e pelo teste unitário de chipRealcado; o histórico de nutrição ficou com a versão 1 (semente), a 1.1 (texto de teste) e a 1.2 (restauração, texto e finalidade idênticos aos da 1, md5 conferido), exceção declarada porque o histórico é só de acréscimo. Dados de teste apagados, zero resíduo. Próximo da fila: {F4-03}, que numera a migração 0052.
RF-CORE-010, RF-CORE-012, ADR-012, L.2, {ART-CFG-01}; teste {TA-100}.
Por que: medido no gate de {F5-03} (06/10/2026): o produto lê corpo JSON em 36 pontos de 23 arquivos e só 9 respondem 400 a JSON malformado ou a corpo que não é objeto; {F5-03} cria o ponto único server/src/http/corpoJson.ts e o aplica em avaliações e anamneses; os demais (incluindo nutrição, que hoje dá 422) ficam inconsistentes. Decisão de escopo da Inspeção.
O que implementar: todas as rotas que leem corpo passam pelo ponto único (400 corpo_invalido para JSON malformado ou corpo que não é objeto), inclusive as 9 que hoje respondem 400 por try/catch próprio; verificador que reprova leitura de corpo fora do ponto único, com decoy; nenhuma mudança de regra de negócio.
Contagem: universo 38 rotas que leem corpo, entre as 52 rotas POST, PUT e PATCH do servidor (medido em 06/10/2026 na main 08b35ed, server/src sem testes); critério comportamental: o tratador chama método de corpo do Hono (req.json, req.text, parseBody, arrayBuffer, blob, formData ou raw.body); 1 leitura por rota, em 23 arquivos, sendo 37 de JSON e 1 multipart. Aritmética: as 37 de JSON são 2 no ponto único ({F5-03}) + 10 com try/catch próprio (fichas 2, sessões 4 com o req.text de /encerrar, solicitações 3, vínculo 1) + 25 que engolem o erro com .catch(()=>...); 2 + 10 + 25 = 37; 37 + 1 upload multipart = 38; 38 + 14 rotas de escrita sem corpo = 52. Lado negativo: as 14 rotas de escrita sem corpo (desativar, reativar, duplicar, aceitar, recusar, cancelar, personificar e renovar sessão) ficam fora do universo, e DELETE e GET também; o multipart de POST /api/admin/exercicios/:id/imagem fica fora da regra 400 (não é JSON), mas a leitura dele passa ao ponto único (lerCorpoMultipart, comportamento idêntico), para o verificador ter zero exceção. RF que reprova: o verificador verificar:corpo imprime 52 = 37 + 1 + 14 e reprova qualquer método de corpo fora de http/corpoJson.ts.
Como verificar (teste primeiro): antes do card, o arnês envia a cada rota que lê corpo JSON malformado e corpo que não é objeto ({x, [], null) e prova o defeito: as 25 a converter respondem outra coisa que 400 (422 ou 500). Depois, as mesmas chamadas dão 400 corpo_invalido sem gravar nada, com controle positivo (corpo válido segue gravando), e o verificador reprova um arquivo sintético que lê o corpo fora do ponto único e passa o que usa o ponto único.
Aceite (H1): Dado um usuário autenticado, Quando envia JSON malformado ou um corpo que não é objeto a qualquer rota de escrita, Então recebe 400 corpo_invalido sem gravar nada, E o verificador reprova uma rota nova que leia o corpo fora do ponto único.
Perda declarada: as respostas 422 a JSON malformado passam a 400; o cliente da própria SPA não depende disso, e isso se confere no gate.
Nota (Inspeção, gate {AUD-13}, Drifts 1 e 2, 2026-10-06): supera o "36" e o "9 que já dão 400" do texto acima (Por que e O que implementar), que ficam como foram publicados. O universo medido é 38, e não 36: o gate achou o req.text de /encerrar (JSON, com try/catch e 400) e o multipart do upload de imagem de exercício, que a primeira medida omitiu. As que já tinham tratamento próprio são 10, e não 9, por mecanismo (try/catch); o vínculo devolve 404 nao_encontrado, então 9 respondem 400 (parametro_invalido) e nenhuma delas corpo_invalido. A aritmética completa está no bloco Contagem acima.
Nota (Inspeção, gate {AUD-13}, Drift 3, 2026-10-06): o Aceite (H1) diz "usuário autenticado", mas a regra vale também para as 6 rotas públicas que leem corpo: sessão (login), cadastro de aluno, cadastro de profissional, conta/confirmar, senha/recuperar e senha/redefinir. Sem sessão, o corpo inválido responde 400 corpo_invalido do mesmo jeito, e o 401, o 403, o 404 e o 429 que hoje vêm antes continuam antes. O H1 não muda; esta nota o estende a essas 6.
Perda declarada, ampliada pelo gate {AUD-13} (Drifts 4, 5, 6 e 7, 2026-10-06): soma-se à Perda declarada acima, e não a substitui. (Drift 4) senha/recuperar: a decisão pinada em senha.test (linha 36: "malformado 202, nunca 400, não vira oráculo") muda para 400 corpo_invalido no malformado. Não vira oráculo, sem revelar se o e-mail existe: o 400 depende só dos bytes de quem chama, nunca do e-mail; o 429 segue antes e {} segue 202. (Drift 5) perdas que o card não previa além de "422 vira 400": o PATCH de perfil com corpo malformado hoje responde 200 (o corpo vira vazio, sem efeito) e passa a 400; null hoje dá 500 nas rotas de administração que leem corpo.x sem ?. e passa a 400; o 410 de conta/confirmar e de senha/redefinir com corpo malformado passa a 400; o 404 do vínculo passa a 400; o 202 de senha/recuperar passa a 400. A tabela por rota, medida no baseline, fica registrada no gate de qa. (Drift 6) 9 rotas trocam parametro_invalido com campo por corpo_invalido (o mesmo 400); o login e o versoes/restaurar de e-mails já respondiam corpo_invalido, e neles muda só a mensagem do malformado. (Drift 7) /api/sessoes/:id/encerrar: o corpo continua opcional (a SPA encerra sem corpo, e vazio segue valendo {}); [], que hoje passa, vira 400.
Ordem: depois de {F5-03}, antes de {F5-04}.
Fechamento [2026-10-06]: entregue e em produção (merge 1c32c5e, repositório wladimirdjr/modularfit, sem migração, https://fit.modulareasy.com (a mesma árvore já provada em produção como ecef515; o deploy da main em 1c32c5e segue em curso); gates plan (APPROVED com 8 drifts, D3, D4 e D5 publicados antes do código), review (rodada 1 APPROVED, 0 Critical, 0 Important) e qa (rodada 1 PASS, 0 P0, 0 P1) verdes). Entregue: o ponto único server/src/http/corpoJson.ts é o único arquivo que toca método de corpo; as rotas de escrita que leem corpo respondem 400 corpo_invalido a JSON malformado, corpo vazio, null, lista, texto e número, na mesma ordem de antes (401, 429, 403, 404 e depois o 400), e {} segue o caminho de hoje; o corpo opcional existe só em /api/sessoes/:id/encerrar. Placar: 52 rotas POST, PUT e PATCH = 37 JSON + 1 multipart + 14 sem corpo; tabela de rotas 38 = 38 no ponto único; 0 leituras fora dele. O verificador verificar:corpo imprime esse placar, reprova método de corpo fora do ponto único e reprova os 13 decoys. H1 provado em produção ({TA-101}): 38 rotas por 6 corpos inválidos dão 400 corpo_invalido sem gravar nada (estado do aluno e do administrador com o mesmo md5 antes e depois), o caminho feliz de cada domínio segue gravando (perfil, consentimento, vínculo, ficha, sessão, refeição, anamnese, avaliação, plano alimentar, exercício e upload) e a SPA segue sem erro de console. Perdas ratificadas: POST /api/vinculos com corpo que não é objeto passa de 403 a 400, com a autorização intacta para corpo válido (aluno e administrador seguem recebendo 403, o profissional segue recebendo 201); [] passa de 404 a 400 em fichas e em /encerrar; null passa de 500 a 400 em 7 rotas; a recuperação de senha com corpo malformado passa a 400 sem revelar se o e-mail existe ({} segue 202 com corpo idêntico para e-mail existente e inexistente). Fora do card, declarado: o qa registrou um P2 preexistente (DELETE /api/admin/exercicios/:id deixa o diretório vazio do exercício no contêiner) e não forçou o 429 do balde antes do 400 nas 3 rotas com balde (cobertura só no teste gerado).
Extensão de {F5-03} sem RF novo; teste {TA-101}.
Fase 3, Treino
Por que esta fase vem aqui. O núcleo do produto é o treino de musculação (Contextualização e J.1 do DRS). Fechadas as fases 1 e 2, que entregam acesso, isolamento, catálogo e vínculo, esta é a primeira fase que entrega valor ao aluno, e é ela que decide se o produto resolve o problema que se propôs a resolver. Dieta (fase 4) e saúde (fase 5) vêm depois por decisão de foco, não por dependência técnica: as duas dependem apenas da fase 2.
RF-TREI-001.
Nota (F1-16): ao criar a tabela da dimensão, o ramo novo entra no CASE de mfit.consumo_do_aluno (não mais em dentro_do_limite_do_aluno, que desde {F1-16} só decide); sem o ramo a função continua levantando exceção, de propósito, e a política nova nasce quebrando, nunca liberada em silêncio; e a tela /plano passa a exibir aquela dimensão sozinha, sem uma linha de código de front, não escrever uma segunda contagem para a tela.
Aceite (H1): Dado um profissional com vínculo ativo, Quando cria uma ficha com título e quatro exercícios com séries, repetições e descanso, preenchendo a observação num dos itens, Então a ficha aparece na lista do aluno com os quatro itens na ordem definida, E o autor registrado é o profissional que a criou, E na execução o item com observação a exibe junto do exercício, E os três sem observação não reservam espaço para ela.
Nota (F1-10): entidade removível de RF-CORE-008. ficha_treino já tem status; ao aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md), este card declara se inativo passa a usar o status existente ou se a tabela ganha desativado_em/desativado_por como as demais (R-2 do gate {F1-10}), além da política FOR DELETE exigindo o papel administrador e do gatilho de remoção, com o verificador de verificarPoliticas.ts rodando antes do gate do qa.
Nota (F1-15): a dimensão fichas_ativas é servida por esta tabela; ao criar a tabela, aplicar a seção "Limite de plano por dimensão" do gabarito e acrescentar o ramo 'fichas_ativas' ao CASE de mfit.dentro_do_limite_do_aluno, sem o ramo a função levanta exceção, de propósito, e a política nova nasce quebrando, nunca liberada em silêncio.
Nota (R-9, {F2-01}): a exclusão definitiva do exercício no catálogo global é livre só enquanto nada o referencia. Quando ficha_item nascer aqui, a coluna exercicio_id entra com REFERENCES mfit.exercicio(id) ON DELETE RESTRICT (nunca CASCADE, para não apagar histórico de treino do aluno), e a tela de administração de exercícios passa a recusar a exclusão com mensagem própria, mantendo a desativação como caminho.
Nota (D-6, {F2-07}): autoria preservada é regra de LEITURA, não de ESCRITA. Ao criar tabela de dado de aluno com coluna autor_id, a política FOR SELECT chama mfit.pode_ler_aluno(aluno_id, autor_id); as políticas FOR INSERT e FOR UPDATE chamam mfit.pode_ler_aluno(aluno_id) sem o segundo argumento, em conjunção com mfit.autor_com_acesso_ativo() (seção "Autoria preservada não é escrita preservada" do GABARITO-ACESSO-ALUNO.md). O critério (e) de ops/verifica-acesso-aluno.mjs reprova qualquer política de escrita que passe autor_id.
Nota (D-3, {F2-08}): autoria também é regra por LINHA, e a conjunção que a garante é diferente da de leitura. Quem chega aqui consulta as DUAS seções do GABARITO-ACESSO-ALUNO.md: "Autoria preservada não é escrita preservada" (a Nota acima, D-6, {F2-07}) e "Autoria por LINHA", que é esta. Por RN-009, só o autor da ficha e o Administrador a editam, e RF-VINC-004 torna o cenário comum, porque o aluno acumula vínculos ativos da mesma especialidade: dois personal trainers ativos ao mesmo tempo, cada um com as próprias fichas. Sem a conjunção AND autor_id = mfit.usuario_atual() na política FOR UPDATE, o UPDATE de um profissional sobre a linha do outro é silenciosamente ACEITO, sem erro e sem aviso, porque mfit.pode_ler_aluno(aluno_id) responde verdadeiro para os dois. A conjunção vai no FOR UPDATE; no FOR INSERT a mesma igualdade existe por outro motivo, garantir que a autoria nasça de quem escreve e que ninguém grave ficha em nome de outro, e as duas não se confundem.
Nota de smoke (gate {F3-01}, Drift 8, 2026-09-14): o catálogo global de exercícios está em 0 de 113 em produção nesta data, e a semente é pendência nomeada fora deste card. Isso NÃO bloqueia o código nem o arnês, que criam os exercícios de que precisam pelas rotas reais, dentro da transação que desfazem; governa o smoke com olhos humanos, porque ninguém monta uma ficha de quatro exercícios numa tela cuja busca não devolve nada, e o smoke reprovaria a tela por um defeito que não é dela. Antes do smoke, o qa cadastra pela tela real de administração de exercícios QUATRO exercícios de grupos musculares diferentes, deixa-os no catálogo (exercício é catálogo global legítimo, não é conta de teste) e registra no relatório do card quais criou. Esperar a semente seria parar a fila do produto por pendência de terceiro, e a própria pendência diz que não bloqueia a fila.
Fechamento [2026-09-14]: merge 17bd22e na main, gates plan, qa e review verdes (processo padrão autônomo), deploy provado em produção (https://fit.modulareasy.com), versões em produção durante o card 58d3727, 4a7b6a1, 90f0db0, d0aafb4, final 17bd22e; migração 0025_ficha_de_treino.sql aplicada, última migração em produção. Seis seções na mesma migração: as tabelas mfit.ficha_treino e mfit.ficha_item (D-2); as três políticas de acesso com as perguntas de autoria separadas, leitura com dois argumentos (aluno_id, autor_id), escrita com um (aluno_id), e autor_id = mfit.usuario_atual() a mais no UPDATE (D-3, D-6 e D-3 de {F2-07} e {F2-08}); os dois gatilhos de remoção na forma de três argumentos, desativação por status (D-1, decisão declarada, ficha_treino NÃO ganhou desativado_em/desativado_por); o ramo fichas_ativas no CASE de mfit.consumo_do_aluno (D-5); exercicio_id com ON DELETE RESTRICT e a rota traduzindo 23503 em 409 exercicio_referenciado, perda declarada e única, o Administrador deixa de excluir em definitivo um exercício referenciado por ficha, com a desativação como caminho equivalente e nomeado na mensagem (D-6, D-14); e as duas funções SECURITY DEFINER do Adendo, mfit.negacao_por_limite_do_aluno e mfit.acesso_do_aluno_ativo, ambas STABLE, guarda única mfit.pode_ler_aluno(p_aluno, NULL, false). Três rotas, nenhuma a mais: POST e GET /api/fichas, GET /api/fichas/:id (D-9). Três telas: criação (/fichas/nova), lista do aluno (/treinos, entrega mínima) e detalhe (/treinos/:id, detalhe da mesma tela de /treinos, não uma décima quinta), as três com caminho de chegada pela interface e componente único do item (ItemDaFicha.tsx, D-11 e D-12). Nome do autor por LEFT JOIN LATERAL mfit.perfil_publico, terceiro chamador nominal, trava síncrona de !== 2 para !== 3, COMMENT ON FUNCTION mfit.perfil_publico(uuid) reescrito na mesma migração (D-10). Verificador do Drift 7 com o critério novo indireto (varredura de information_schema.columns não alcança as seis tabelas de forma 2, sem aluno_id) e decoy que reprova de verdade: placar 1 servida + 5 pendentes = 6 tabelas de forma 2. Placares honestos colados pelo qa: acesso 2 de 18 tabelas com autor_id cobertas pelo critério (e); remoção 2 de 11 entidades com o gatilho genérico; entidades sensíveis inalteradas; inativo (entidades removíveis com coluna de estado declarada) 5 + 16; consumo_do_aluno 2 de 6 dimensões com ramo no CASE; chamadores nominais de mfit.perfil_publico 3 de 3. O verificador de exercícios (ops/verifica-exercicios.mjs) passou a detectar citação de mfit.exercicio em qualquer posição da consulta, não só no FROM, fechando dois pontos cegos achados no qa e no code-reviewer durante este card. Uma perda declarada e intencional (D-14); nenhuma outra: nenhum papel perde capacidade, nenhuma rota existente muda de resposta, nenhum placar existente muda de valor fora dos de cobertura. {TA-24} continua [ ], as duas primeiras cláusulas do "Passa quando" provadas aqui, as duas últimas aguardando {F3-05} (Nota já publicada em {TA-24}, Drift 4).
RF-TREI-002 e ADR-002.
Aceite (H1): Dado um aluno sem nenhum vínculo ativo, Quando cria uma ficha com três exercícios, Então a ficha é salva com autoria dele e fica disponível para execução, E aparece para os profissionais vinculados depois, identificada como criada pelo aluno.
Nota de escopo (gate {F3-02}, Drift 2, 2026-09-14): o que entra neste card: o próprio aluno cria ficha para si, sem vínculo nenhum, pela MESMA tela /fichas/nova que {F3-01} construiu (a Nota do Apêndice de catálogo de telas já decidiu que a criação tem uma tela só servindo os dois papéis, e nenhum prefixo por papel nasce aqui); e o profissional lê essa ficha pela MESMA tela /treinos, com o aluno na consulta, chegando até ela pela linha do aluno em /alunos. Isso reconcilia a implementação ao catálogo, nunca o contrário: o Apêndice de catálogo de telas do DRS já declara /treinos e /fichas/nova servindo Aluno, Profissional, e foi a implementação de {F3-01} que estreitou uma ao papel aluno e a outra ao papel profissional. O requisito não é editado, o ADR não é editado e o Apêndice de telas não é editado: os três já estão certos. O que NÃO entra: edição de ficha ({F3-03}), pedido de ajuste e solicitação de ficha nova ({F3-04}), duplicação ({F3-09}), execução ({F3-05}), histórico ({F3-07}), preferências ({F3-08}), a tela de detalhe do aluno ({F7-07} a {F7-11}) e a geração assistida por IA (Fase 8, {F8-05} e {F8-12}), esta última em nenhuma forma, nem botão desabilitado, nem rótulo "em breve", nem banner: ausência é ausência. A criação NÃO se abre ao Administrador: a Matriz de papéis deriva "criar ficha de treino" de RF-TREI-001 e RF-TREI-002, e ele não está em nenhum dos dois (RN-009 lhe dá a edição, não a criação), então o guarda de papel é fechado em lista, nunca removido. Placares de tela: {ART-SCR-01} e {ART-SCR-02} não mudam, porque nenhuma tela nova nasce e /treinos e /fichas/nova já são contadas uma vez cada, em {ART-SCR-01}, pelo precedente de /perfil. Placar de banco: nenhum, este card não cria tabela nem migração, e a última migração em produção continua 0025.
Nota do rótulo de autoria (gate {F3-02}, Drifts 3 e 4, 2026-09-14): a pergunta "fui eu que criei?" é sobre o LEITOR; o campo que o servidor calcula responde uma pergunta sobre a LINHA, "o autor é o titular da ficha?". Até este card as duas coincidiam por acidente, porque nenhuma ficha em produção tinha o aluno como autor, e por isso o detalhe da ficha dizia "Criada por você" a QUALQUER leitor, inclusive ao profissional que abrisse a ficha de um aluno; a lista, do outro lado, nunca consultava o campo e mostrava o nome do autor, de modo que a mesma ficha já se descrevia de dois jeitos para o mesmo leitor. Não é defeito de texto: é a assinatura de que a regra mora em dois arquivos de tela e só um deles sabe que ela existe. A partir daqui o rótulo tem ponto único, um módulo só, com função pura que decide pelo LEITOR antes de decidir pela linha, nesta ordem: o leitor é o autor, "Criada por você"; o autor é o titular da ficha, "Criada pelo aluno"; caso contrário, o nome do autor com o registro quando houver; sem nome, "Autor não disponível". A identidade do leitor vem da sessão, nunca de comparação de identificadores feita na tela e nunca de uma segunda consulta. O ponto único é protegido por critério de verificador próprio, com lista nominal fechada de arquivos autorizados a ler o campo, trava de cardinalidade que recusa carregar o módulo se a lista crescer sem decisão, e decoy visto reprovando antes de ser desfeito. Esta Nota governa os sete cards futuros que a Nota (R-4, cinco lugares de D.2) já nomeia como exibidores de autoria, {F3-05}, {F3-07}, {F3-09}, {F5-01}, {F5-04}, {F5-05} e {F5-06}: cada um consome a mesma função e a mesma leitura reduzida, nunca um SELECT próprio e nunca um if novo na tela onde o defeito aparecer. {TA-25} não ganha Nota: ele fecha inteiro neste card, porque as duas metades do "Passa quando" (a ficha salva com autoria do aluno, e a mesma ficha aparecendo ao profissional vinculado depois, identificada como criada pelo aluno) passam a ser servíveis pela interface aqui.
Fechamento [2026-09-14]: merge 3d25e27 na main, gates plan, qa e review verdes (processo padrão autônomo, .claude/gates/{plan,qa,review}/modularfit-F3-02.gate), deploy provado em produção (https://fit.modulareasy.com), versões em produção durante o card 7c9f722, 7c2f833, 28e97bf, final 3d25e27; ZERO migração, última em produção continua 0025_ficha_de_treino.sql (D-3, provado em Postgres efêmero na VPS, conjunção por conjunção). Guarda de criarFicha fechado em lista de papéis (profissional, aluno), nunca por exclusão (D-1); Administrador recebe 403 com mensagem nomeando os dois papéis que criam. alunoId opcional só para o titular, com 403 na camada 1 antes do banco quando o aluno informa alunoId alheio (D-2). Rótulo de autoria com ponto único em web/src/fichas/autoria.ts, decidindo pelo LEITOR antes da LINHA, consumido pelos dois lugares que hoje exibem autoria (D-5); décimo sétimo verificador ops/verifica-rotulo-autoria.mjs, lista nominal fechada, trava de cardinalidade e decoy visto reprovando (D-11). /treinos passa a servir os dois papéis por ?aluno=, com o link novo na linha do aluno em /alunos (D-4); /fichas/nova ganha o ramo do aluno, sem seletor e sem GET /api/vinculos para ele (D-9); três caminhos de chegada novos, painel do aluno, topo e vazio de /treinos, e a linha de /alunos (D-10). Achado do qa durante o card, corrigido no mesmo card: FichaDetalhe não tratava aluno_id no caminho de volta por papel, ajustado para o profissional voltar a /alunos e o aluno a /treinos. Perda declarada e única (D-13): POST /api/fichas deixa de responder 403 "Somente o profissional cria..." ao papel aluno, e o caso de teste homônimo foi invertido, não apagado, passando a provar a aceitação do aluno e a recusa do alunoId alheio; o caso irmão do Administrador permanece. {TA-25} fecha inteiro (as duas metades do H1 provadas pela interface real). Nenhum outro papel perde capacidade, nenhuma rota existente muda de resposta fora da declarada.
RF-TREI-003, ADR-006 e RN-009.
Aceite (H1): Dado uma ficha criada por um Personal Trainer, Quando o aluno dono tenta alterar séries, repetições, ordem ou lista, Então a operação é negada e as ações oferecidas são consultar, pedir ajuste e duplicar, E quando o autor faz a mesma alteração ela é gravada e produz auditoria com valor anterior e novo.
Nota de escopo (gate {F3-03}, Drifts 7, 8 e 9, 2026-09-14): o que ENTRA neste card: a edição da ficha pelo autor e pelo Administrador (RN-009 dá a edição aos dois, e é o banco de hoje que recusa o Administrador, o que se corrige na migração 0026, nunca no requisito); a negação ao não autor nas duas camadas (ADR-013), com a única ação que existe hoje, consultar; a auditoria da alteração com valor anterior e valor novo pelo gatilho genérico, que é o que o H1 cobra por escrito; e a desativação e a reativação da ficha, que entram aqui porque o mecanismo existe no banco desde {F3-01} e nenhum card posterior da Fase 3 o reivindica, medido card a card de {F3-04} a {F3-10}: se não nascer aqui, não nasce na fase, e o teto de fichas ativas fica beco sem saída para o aluno no limite. O que NÃO entra: pedir ajuste, que é a solicitação de RF-TREI-004 e tem card dono {F3-04} (a entidade solicitacao_ficha e o cartão {ART-SM-03} já declaram isso, e o próprio RF-TREI-003 remete a duplicação a RF-TREI-009 pelo texto); duplicar, RF-TREI-009, card dono {F3-09}; execução ({F3-05}); histórico ({F3-07}); preferências ({F3-08}); retomada ({F3-10}); e a notificação de prescrição alterada ({ART-WF-06}), em nenhuma forma. Ao não autor a tela oferece apenas consultar: nenhum botão desabilitado, nenhum rótulo "em breve", nenhum banner explicando que a ação chega depois. Ausência é ausência, e oferecer as duas ações que ainda não existem seria mentir na tela para fechar um caso de teste. O título do card não é editado: ele descreve a intenção do produto, e esta Nota descreve a fronteira da entrega. A edição não é tela nova: é ESTADO da tela de criação /fichas/nova, alcançado pelo caminho /fichas/:id/editar, no mesmo precedente já usado por /exercicios/:id e /treinos/:id, e por isso {ART-SCR-01} e {ART-SCR-02} não mudam de placar. O requisito não é editado, o ADR não é editado e RN-009 não é editada: os três já estão certos, e quem diverge deles é o banco. {TA-09} fecha inteiro neste card; {TA-26} fica [ ] com Nota nominal, no precedente literal de {TA-24}; {TA-08} sobe de placar sem fechar. Placar de banco: nenhuma tabela nova, e a última migração em produção passa de 0025 para 0026 no fechamento.
Fechamento [2026-09-14]: merge 76bc9cd na main, gates plan, qa e review verdes (processo padrão autônomo, .claude/gates/{plan,qa,review}/modularfit-F3-03.gate), deploy provado em produção (https://fit.modulareasy.com), versão em produção 5d5b2a0, final 76bc9cd; migração 0026_edicao_da_ficha.sql aplicada, última em produção passa de 0025 para 0026. Auxiliar mfit.pode_editar_por_autoria libera o Administrador nas políticas de UPDATE das duas tabelas e no USING da nova política de DELETE de ficha_item; restrição ficha_item_ficha_ordem_unq recriada como DEFERRABLE; gatilhos de auditoria de criação e de alteração instalados nas duas tabelas, sem linha dobrada na transição de estado. PATCH /api/fichas/:id edita numa transação única, reconciliando a lista de itens por identificador (item conhecido com diferença recebe UPDATE, sem identificador recebe INSERT, ausente no corpo recebe DELETE, identificador alheio é recusado antes do banco); status fica fora do PATCH, com desativar e reativar em rotas próprias. /fichas/:id/editar é ESTADO da mesma tela /fichas/nova, sem tela nova. Critério de tabela alterável com auditoria de alteração fecha 7 servidas + 2 declaradas = 9 de 9. Perda declarada e única: o UPDATE do Administrador deixa de ser recusado pela política, RN-009 servida pelas duas camadas. {TA-09} fecha inteiro; {TA-26} continua [ ] com Nota nominal, a metade que falta depende de {F3-09} (duplicar, pendente); {TA-08} sobe de 1 de 11 para 2 de 11, sem fechar. Nenhum outro papel perde capacidade.
RF-TREI-004.
Aceite (H1): Dado um aluno com vínculo ativo, Quando envia solicitação com motivo preenchido, Então o profissional recebe a notificação e vê a solicitação aberta, E ao criar a ficha vinculando a solicitação o estado passa a atendido e o aluno recebe a notificação com a ficha nova.
Nota de escopo (gate {F3-04}, 2026-09-14): o que ENTRA neste card: as duas formas de pedido que RF-TREI-004 descreve no próprio texto, a ficha nova e o ajuste de uma existente, cada uma com a sua pergunta gravada em coluna própria; o atendimento pelo profissional destinatário, criando a ficha ou alterando a que é dele, com o estado passando a atendida; a recusa com motivo obrigatório; os três avisos do fluxo {ART-WF-05}, por gatilho na origem, no molde dos três avisos de vínculo que já estão em produção; e o acompanhamento do próprio pedido pelo aluno, em forma mínima, que a jornada dele cobra por escrito. O que NÃO entra: duplicar, que é RF-TREI-009 e tem card dono {F3-09}; a execução ({F3-05}), o cronômetro ({F3-06}), o histórico ({F3-07}), as preferências ({F3-08}) e a retomada ({F3-10}), e por isso sessao_treino, sessao_serie e preferencia_treino não nascem aqui e {ART-ENT-04} para em 3 de 6; a central de notificações com sino, contagem e limpar todas, que é {F6-05} e {ART-ENT-07}; a notificação de prescrição alterada ({ART-WF-06}), em nenhuma forma, cujo dono continua sendo {F6-05}; o painel do profissional com os cinco indicadores, a carteira, o alerta de inativo e o bloco por especialidade, que é {F7-07}, de modo que {ART-DASH-02} continua [ ] e {ART-SCR-02} continua 1 de 4; e a geração assistida, que é da Fase 8, em nenhuma forma, nem botão desabilitado, nem rótulo "em breve", nem banner: ausência é ausência. O título do card não é editado, e nenhum requisito, regra de negócio ou decisão de arquitetura é editada: o que se reconcilia são três descrições que estavam incompletas diante do próprio RF, a especificação da entidade em E.1 e em {ART-ENT-04}, a máquina de estados em E.3 e a Matriz Ator por Capacidade, mais as notas de fronteira do Inventário. Os quatro caminhos são estado e caminho de telas já contadas, nunca rota nova de catálogo: pedir ficha nova e acompanhar vivem em /treinos, pedir ajuste vive em /treinos/:id, ver e resolver vivem em /pro/painel, e atender leva a /fichas/nova com a solicitação na consulta; é o mesmo precedente já firmado por /exercicios/:id, por /treinos/:id e pelo estado de edição em /fichas/:id/editar, e por isso {ART-SCR-01} continua 5 de 14 e {ART-SCR-02} continua 1 de 4. O bloco do profissional nasce mínimo, no precedente literal da /alunos mínima de {F2-05} e do bloco Vínculos dentro de /perfil de {F2-06}. Donos nominais do que fica de fora: duplicar é {F3-09}, a central de notificações é {F6-05}, o painel completo é {F7-07}. {TA-27} fecha inteiro neste card; {TA-26} continua [ ] com Nota atualizada, porque duplicar continua ausente; {TA-09}, {TA-24} e {TA-25} não são tocados. A tela de detalhe da ficha, que desde {F3-03} oferecia ao não autor apenas consultar, passa a oferecer consultar e pedir ajuste, e essa é a única mudança de comportamento que este card declara.
Fechamento [2026-09-14]: merge 8f0f9fd na main, gates plan, qa e review verdes (processo padrão autônomo, .claude/gates/{plan,qa,review}/modularfit-F3-04.gate), deploy provado em produção (https://fit.modulareasy.com), versão em produção 8f0f9fd; migração 0027_solicitacao_de_ficha.sql aplicada, última em produção passa de 0026 para 0027. Ficou pronto: as duas formas de pedido (ficha nova e ajuste de uma existente) em colunas próprias (ficha_alvo_id, ficha_id), com motivo_recusa, criado_em e resolvido_em; máquina de estados por gatilho BEFORE UPDATE OF estado, recusando por construção qualquer volta a aberta; três avisos por gatilho na origem (criação, atendimento e recusa), cobrindo por transição os dois caminhos de atendimento, criar ficha ou alterar a existente; quatro rotas (POST /api/solicitacoes, GET /api/solicitacoes, atender, recusar), camada 1 com listas fechadas por papel e o destinatário resolvendo o próprio pedido; os quatro caminhos de tela (/treinos, /treinos/:id, /pro/painel, /fichas/nova?solicitacao=), nenhuma rota nova de catálogo. {TA-27} fecha inteiro; {TA-26} continua [ ] com a Nota atualizada; {ART-WF-05} fecha [x]; {ART-ENT-04} passa a 3 de 6; {ART-SM-03} continua [ ]. Única mudança de comportamento declarada (D-16): a tela de detalhe da ficha, que desde {F3-03} oferecia ao não autor apenas consultar, passa a oferecer consultar e pedir ajuste. Nenhum outro papel perde capacidade.
RF-TREI-005.
Nota (R-15, {F2-02}): a exibição da mídia na execução da sessão é o ponto 2 de 2 de {TA-21} e o ponto 2 de 2 de {TA-20}. Este card não reimplementa a leitura da mídia nem o cálculo de proporção: consome o mesmo componente único de mídia de {F2-02}, e o que fecha aqui são os dois placares, não um desenho novo.
O que implementar: navegação exercício a exercício com a observação do item visível, cronômetro geral, registro de série aceitando carga e repetições ou tempo de execução em segundos, observação por série e observação da sessão, pular exercício, encerrar a qualquer momento, e bloqueio de tela do dispositivo enquanto a sessão está em andamento, liberado ao encerrar ou ao sair da tela.
Como verificar (teste primeiro): executar a ficha no celular e deixar a tela ociosa durante a sessão, conferindo que ela não escurece onde o navegador concede o bloqueio, e que a ausência do recurso não gera erro nem interrompe a sessão.
Aceite (H1): Dado um aluno com ficha de cinco exercícios, Quando inicia a sessão, registra três séries de carga no primeiro, pula o segundo, registra no terceiro uma série por tempo de sessenta segundos sem repetições e conclui os demais, Então a sessão registra duração maior que zero, um exercício pulado, as séries de carga completas e a série por tempo com os sessenta segundos e repetições vazias, sem erro de validação, E o volume soma apenas as séries com carga e repetições, ignorando a série por tempo (RN-004), E as observações da série e da sessão aparecem no histórico, E durante a sessão a tela do dispositivo não escurece por inatividade em todo navegador que conceda o bloqueio.
Nota de escopo (gate {F3-05}, 2026-09-14): o card entrega RF-TREI-005 inteiro e nada além dele. Entra: as duas tabelas da execução (sessao_treino e sessao_serie), com a série gravada a cada registro; a navegação exercício a exercício com mídia, instruções de execução, erros comuns, dicas de execução e a observação do item; o cronômetro geral; o registro de série de carga e de série por tempo; a observação por série e a da sessão; pular exercício; encerrar a qualquer momento; e o bloqueio de tela do dispositivo. Não entra, com dono nominal: o cronômetro de descanso com aviso sonoro é RF-TREI-006 e {F3-06}; a carga anterior e a evolução por exercício são RF-TREI-007 e {F3-07}, e a tela de histórico também; o atalho de retomar no painel e na lista, a retomada e a passagem automática a abandonada são cláusulas do H1 de RF-TREI-010 e {F3-10}; as preferências de treino são {F3-08}; o painel do aluno, e com ele o "treino do dia", é {F7-06}. Ausência é ausência: nada de botão desabilitado, rótulo "em breve" ou banner para o que tem outro dono. Rota: /treinos/sessao é linha própria do Apêndice de catálogo de telas do DRS, com papéis, elementos e estados declarados, e {ART-SCR-01} a relaciona nominalmente a este card; ela não é detalhe de /treinos, ao contrário de /treinos/:id e de /fichas/:id/editar, que os gates de {F3-01} e de {F3-03} declararam como detalhe e estado. Por isso ela é declarada antes de /treinos/:id no arranjo de rotas: o padrão de identificador casa com o literal sessao em qualquer roteador que resolva por ordem de declaração. Persistência: a gravação da sessão e da série no banco a cada registro nasce aqui, por construção da tabela, e não em {F3-10}, cujo "O que implementar" foi reconciliado nesta data. Aritmética: {ART-ENT-04} passa de 3 de 6 para 5 de 6 tabelas e {ART-SCR-01} de 5 de 14 para 6 de 14 telas, e os dois placares só se movem no fechamento, medidos no dia, nunca projetados aqui.
Fechamento [2026-09-15]: merge 80c103e na main, gates plan, qa e review verdes (processo padrão autônomo, .claude/gates/{plan,qa,review}/modularfit-F3-05.gate), deploy provado em produção (https://fit.modulareasy.com), versões em produção 1ac391a e depois 2007262, versão corrente 80c103e; migração 0028_execucao_da_sessao.sql aplicada, última em produção passa de 0027 para 0028. Ficou pronto: as duas tabelas da execução, sessao_treino sem autor_id (Drift 2 corrigido) e sessao_serie; gatilho único que decide o resultado do encerramento pela contagem de séries registradas (concluída ou abandonada), gravando duração, volume por RN-004 e ultimo_registro_em no mesmo ponto; navegação exercício a exercício com mídia, instruções, erros comuns, dicas e observação do item pelo componente único; cronômetro geral; registro de série de carga e de série por tempo; observação por série e da sessão; pular exercício; encerrar a qualquer momento; bloqueio de tela com degradação silenciosa; rota própria /treinos/sessao, declarada antes de /treinos/:id; décimo oitavo verificador (ops/verifica-sessao.mjs). Placar de exibição de texto (verificador 14) medido em 2 de 2; placar de exibição de mídia (verificador 15) medido em 1 de 2, porque o catálogo em produção não tem nenhum exercício com mídia cadastrada, pendência do dono em docs/seed/midia/: {TA-21} continua [ ]. ENTIDADES_ESCRITA_ALUNO sobe de 22 para 24 (Drift 5, ficha_item e sessao_serie); alteração auditada fecha em 12 de 12; forma indireta fecha em 2 servidas + 4 pendentes = 6; autoria de terceiro passa de 11 para 10 pendências (Drift 2). {TA-28} fecha inteiro; {TA-24} fecha inteiro; {TA-20} fecha inteiro; {TA-21} continua [ ] com a Nota; {ART-ENT-04} passa a 5 de 6; {ART-SCR-01} passa a 6 de 14; {ART-SM-03} continua [ ]; {ART-WF-04} continua [ ]. Três mudanças de comportamento declaradas (D-17): /treinos e /treinos/:id passam a oferecer iniciar a sessão; /exercicios/:id passa a renderizar pelo componente único, saída visual inalterada; ficha_item passa a ter a escrita do aluno inativo verificada. Nenhum outro papel perde capacidade.
RF-TREI-006.
Aceite (H1): Dado um exercício com descanso de sessenta segundos, Quando o aluno conclui uma série, Então a contagem começa em sessenta e decresce, E ao chegar a zero emite o aviso e libera a série seguinte, E o botão de pular encerra a contagem imediatamente.
Nota de escopo (gate {F3-06}, 2026-09-15): o que ENTRA neste card: a contagem regressiva a partir do alvo definido em ficha_item.descanso, lido de ItemDaSessao.descanso_segundos; o aviso sonoro ao chegar a zero; o aviso visível e anunciável que acompanha o som; o botão de pular; e o botão de ajustar o tempo na hora (+15 s) que a Descrição de RF-TREI-006 exige e o mockup materializa, a quarta obrigação que o H1 não nomeia mas a Descrição sim. Cinco comportamentos que nenhum artefato declarava e que o mockup decide: o descanso não começa quando não há série seguinte a liberar, nem quando o alvo é zero; pular exercício, navegar para outro exercício, encerrar a sessão e sair da tela cancelam o descanso sem aviso sonoro; a barra mostra o tempo restante e esvazia; e o descanso não persiste, de modo que recarregar a página no meio dele volta ao bloco de registro. O que NÃO entra, com dono nominal: nenhuma coluna, migração, tabela, índice, restrição, política, gatilho, função de banco ou rota; nenhum parâmetro de operação; e a vibração, que não está em requisito, em inventário, em caso de teste nem no mockup, e por isso não entra em forma nenhuma. A preferência do aluno de silenciar o aviso sonoro não existe em artefato nenhum hoje: está registrada em {Q2} do Questionário, sem bloquear a fila. O título do card não é editado e o H1 do card não é editado.
Nota de fechamento (gate {F3-06}, 2026-09-15): merge f21943f em main, prod f21943f, zero migração, 100 por cento web (web/src/sessoes/descanso.ts, 61 testes web); fonte da verdade é o instante de fim; +15 s; pular; aviso visível com região viva e som por Web Audio sem asset externo; vibração fora, ver {Q2}. {TA-29} continua [ ]: três das quatro cláusulas fecharam com dado real no smoke (começa em sessenta e decresce; o ajuste +15 s; o botão de pular), a quarta (zerar, emitir o aviso e liberar a série seguinte, com a metade de acessibilidade sem som) fechou só a metade sonora, e a metade de acessibilidade não foi testada isoladamente nesta rodada. Gates plan, qa e review verdes.
RF-TREI-007.
Aceite (H1): Dado um aluno com carga máxima anterior de quarenta quilogramas no supino, Quando conclui uma sessão com quarenta e cinco, Então o histórico exibe a variação de mais cinco naquele exercício, E a primeira execução de um exercício exibe a marcação de primeira medição em vez de variação.
Nota de escopo (gate {F3-07}, 2026-09-15): o card entrega RF-TREI-007 inteiro, as oito obrigações da Descrição e do H1, em três pontos de entrega: a tela /treinos/historico, com uma linha por sessão concluída trazendo data, ficha, duração, volume e contagem de séries, mais um chip de variação por exercício; o bloco de evolução por exercício no resumo da execução, com o link Ver histórico no lugar do comentário datado que hoje declara o beco sem saída; e a carga máxima anterior exibida durante a execução, ao lado do descanso alvo, elemento que o desenho aprovado já atribuía a este card por nome e que a Descrição de RF-TREI-007 passou a nomear nesta data. A progressão é derivada na leitura, em ponto único, nunca agregado gravado: nenhuma coluna, tabela, índice, restrição, política, gatilho, função de banco ou migração nasce aqui. O chip tem quatro estados (subiu, caiu, igual e primeira medição) e o quinto não nasce: exercício cuja sessão só teve tempo, ou que foi pulado, simplesmente não tem chip, pela regra terminal de RN-004, cálculo não exibido e nunca em zero; carga zero registrada de propósito é valor, não ausência, e entra na cadeia. A janela de retenção do histórico visível do plano passa a ser aplicada aqui, na leitura, pelo mesmo ponto único, com o valor ilimitado não filtrando nada, e a comparação de carga continua sobre todo o histórico do aluno, porque a janela decide quais sessões são listadas e nunca quais fatos são verdadeiros; a decisão e os dois cenários estão registrados em {Q3} do Questionário, e a dimensão de retenção do histórico visível, que até aqui não tinha card dono em nenhuma das oito fases, passa a ter este card como dono declarado. Cinco comportamentos que nenhum artefato declarava e que este card decide: a sessão abandonada não entra na lista; a linha clicada leva ao resumo daquela sessão; o profissional vinculado abre a mesma tela com o aluno na consulta, chegando por /treinos; os chips vêm um por exercício com série registrada, na ordem dos itens da ficha; e a barra de resumo mostra a contagem de sessões e a soma de volume do filtro corrente. Sem paginação, porque nenhum artefato a declara para esta tela: o volume se resolve pelo filtro de período com teto de linhas e o total visível, de modo que teto atingido se veja em vez de truncar calado; se vier a ser necessária, é card novo. O que NÃO entra, com dono nominal: caixa de seleção por linha, Selecionar todas e ação em lote são RF-PAIN-010 e {F7-16}; o gráfico de progressão de carga por exercício é RF-ENGA-005 e {F7-05}; melhor marca, recorde e marco pessoal não estão em artefato nenhum e não nascem, sob pena de requisito nascido num gate; sequência de dias e calendário são {F7-02} e {F7-03}; conquistas são {F7-04} e {F7-05}; atalho de retomar, retomada e passagem automática a abandonada são {F3-10}; listar sessão abandonada no histórico não tem dono hoje e, se o dono quiser, é card novo; cronômetro de descanso e aviso sonoro são {F3-06}; preferências de treino são {F3-08}; duplicação de ficha é {F3-09}; painel do aluno e treino do dia são {F7-06}; a tela de detalhe do aluno para o profissional é {F7-07} a {F7-11}; exportação do histórico em arquivo é RF-CORE-007 e card da Fase 8; notificação, sino e contagem são {F6-05}; geração assistida é da Fase 8, {F8-05} e {F8-12}. Em todas, ausência é ausência: sem botão desabilitado, sem rótulo "em breve", sem banner. Aritmética de placares, que só se movem no fechamento, medidos no dia: {ART-SCR-01} passa de 6 de 14 para 7 de 14 com /treinos/historico, que é linha própria do Apêndice de catálogo de telas e não detalhe de /treinos; {ART-WF-04} passa de 2 de 5 para 3 de 5 passos e o cartão segue [ ]; {ART-SCR-02} continua 1 de 4, porque a tela serve os dois papéis e é contada uma vez só, no precedente literal de /perfil, /treinos e /fichas/nova; {ART-ENT-04} continua 5 de 6 tabelas; {ART-CFG-01} continua com vinte e oito parâmetros; {ART-SM-03} continua [ ], porque o card lê o estado concluída e não produz transição; {ART-DASH-01} e {ART-DASH-02} continuam [ ]; {TA-30} fecha inteiro, e só depois de as seis cláusulas que faltavam entrarem no "Passa quando". O título do card não é editado e o H1 do card não é editado.
Nota de fechamento (gate {F3-07}, 2026-09-15): Fechamento [2026-09-15]: merge c331286 na main, gates plan, qa e review verdes (.claude/gates/{plan,qa,review}/modularfit-F3-07.gate), deploy provado em produção (https://fit.modulareasy.com), versões em produção durante o card 7b24dfa, 07cfe0b, final c331286; ZERO migração, última em produção continua 0028. GET /api/sessoes (só sessões concluídas, teto 50 com o total na mesma consulta, janela limite_historico_dias do plano aplicada na leitura, {Q3} Cenário 1); progressão de carga em ponto único server/src/sessoes/progressao.ts sobre TODO o histórico (a janela filtra a listagem, nunca a cadeia: I1 do review, D-8/D-14); carga_anterior_kg por item na execução; sexto critério do verificador de sessão (150 arquivos varridos, decoy visto reprovando); guarda de carregamento de dimensoes.ts estreitada (seção 8 do gate). Tela /treinos/historico (quatro estados de chip na família .mf-etiqueta), bloco de evolução no resumo da execução com Ver histórico, Anterior no item em execução. Testes do web 61 -> 74; server 951 casos. Perda declarada: nenhuma. Achado do qa corrigido no card: chip sem espaço entre nome e valor (gap na família .mf-etiqueta).
RF-TREI-008.
Aceite (H1): Dado um aluno na tela de preferências, Quando escolhe dois objetivos, nível intermediário, cinco equipamentos, três dias e sessenta minutos e salva, Então os valores aparecem preenchidos ao reabrir, E o profissional vinculado enxerga as mesmas preferências na ficha do aluno.
Nota de escopo (gate {F3-08}, 2026-09-15): o card entrega RF-TREI-008 inteiro, as oito obrigações da Descrição e do H1, e nada além: os sete campos do Apêndice de catálogo de formulários, com objetivos e nível obrigatórios e os outros cinco opcionais (RN-002, todo o restante nasce nulo), as três listas fechadas do Apêndice (oito objetivos, três níveis, vinte e seis equipamentos), os dias da semana de segunda a domingo, a duração de 20 a 120 minutos com passo de 5, e os dois textos de até 2000 caracteres cada. A tela /treinos/preferencias passa a existir em catálogo: ela não tinha linha no Apêndice de catálogo de telas do DRS nem em {ART-SCR-01}, apesar de o H1 falar em "tela de preferências", e essa reconciliação foi publicada nesta data, mudando o universo do cartão de telas do aluno de catorze para quinze. Ela é uma só e serve os dois papéis, no precedente literal de /perfil, de /treinos, de /fichas/nova e de /treinos/historico: o profissional vinculado chega à mesma rota com o aluno na consulta e a vê em somente leitura de verdade, com os valores e sem controle nenhum, nunca um formulário desabilitado, e o estado vazio dele tem texto próprio, porque dizer "preencha suas preferências" a quem não pode preencher é mentira de tela. A escrita é exclusiva do titular, numa rota que nunca aceita identificador de aluno, e por isso a Matriz Ator por Capacidade ganhou nesta data duas linhas e não uma, registrar e ler, com marcas diferentes. O ponto de entrada é um botão Preferências no cabeçalho de /treinos, ao lado do Histórico, com a mesma regra de parâmetro, porque os dois pontos de entrada que o desenho aprovado tem para esta tela são de outros cards, o passo de primeiros passos do painel ({F7-06}) e o resumo da geração assistida (Fase 8), e sem um terceiro a tela nasceria inalcançável. Preferência não guarda série, e isso é decisão declarada, não omissão: aluno_id é único, é uma linha por aluno, M.3 manda ler sempre o valor vigente, e o valor anterior de cada alteração já fica na trilha imutável de RF-CORE-009, gravada pelo gatilho genérico; se o dono quiser uma tela de histórico de preferências, é card novo. O que NÃO entra, com dono nominal: o botão Salvar e gerar treino que o desenho põe dentro desta mesma tela, a tela de gerar treino e qualquer leitura das preferências pela geração assistida são {F8-05} e {F8-12}; a correspondência entre a lista fechada de vinte e seis equipamentos do aluno e o equipamento em texto livre do exercício do catálogo não existe hoje em artefato nenhum e não nasce aqui, e o dono dela é {F8-05}, que é quem precisa cruzar as duas para cumprir o H1 de RF-IAGE-001; o bloco de primeiros passos do painel e a rotação de fichas por preferência são {F7-06}; a tela de detalhe do aluno é {F7-07} a {F7-11}, e quando ela nascer a aba de treino linka para esta rota em vez de redesenhar o bloco; notificar o profissional quando a preferência muda é {F6-05}, e {ART-WF-06} não menciona preferência; materializar o parâmetro de listas fechadas em tabela editável é do card que vier a ser dono de {ART-CFG-01}, que tem 1 de 28 materializado; exclusão da preferência não é de ninguém, porque a entidade não está entre as onze de RF-CORE-008 e o aluno esvazia editando. Em todas, ausência é ausência: sem botão desabilitado, sem rótulo "em breve", sem banner. Duas declarações que o card faz em vez de corrigir: o mesmo fato clínico é dado sensível quando vive na anamnese e não é sensível quando o aluno o escreve no campo de limitações e lesões, porque RN-001 fecha a lista em seis categorias e preferência não está nelas; a assimetria passa a estar escrita em RN-001 e nas Notas do requisito, a tela avisa o aluno do alcance do campo antes de ele escrever, e a pergunta de produto sobre restringir esse campo por especialidade está em {Q4} do Questionário, sem bloquear a fila. E {Q2}, sobre silenciar o aviso sonoro do cronômetro, também não bloqueia: o card entrega o Cenário 1, e se o dono responder o Cenário 2 a emenda é uma coluna e um controle, em card novo. Aritmética de placares, que só se movem no fechamento, medidos no dia: {ART-ENT-04} fecha em 6 de 6 tabelas com a sexta e última; {ART-SCR-01} passa a 8 de 15, universo já crescido nesta reconciliação; {ART-FORM-03} fecha em 7 de 7 campos, com o campo Estado que ele não tinha; {ART-SCR-02} continua 1 de 4; {ART-CFG-01} continua com vinte e oito parâmetros, nenhum nasce, nenhum é materializado e nenhum é lido; {ART-WF-12} continua [ ]; {TA-31} fecha inteiro, e só depois de as cláusulas que faltavam entrarem no "Passa quando"; {TA-06} sobe, com o placar impresso pelo arnês no dia e nunca presumido; {TA-10} continua 0 de 6, porque a entidade não é sensível. O título do card não é editado e o H1 do card não é editado.
Nota de fechamento (gate {F3-08}, 2026-09-15): Fechamento [2026-09-15]: merge ce1787b na main, gates plan, db, qa e review verdes (.claude/gates/{plan,db,qa,review}/modularfit-F3-08.gate), deploy provado em produção (https://fit.modulareasy.com), versões em produção durante o card 10b354b, dfbc498, final ce1787b; migração 0029_preferencia_treino.sql aplicada (3 enums, tabela com 11 colunas, 8 CHECKs, UNIQUE por aluno, RLS com escrita só do papel aluno, REVOKE DELETE, auditoria reusada), última em produção passa de 0028 para 0029. GET/PUT /api/preferencias (upsert único com deduplicação e ordem crescente de dias; 422 por campo; 403 nomeado a administrador e profissional na escrita; ?aluno= só na leitura pelo vinculado) e GET /api/preferencias/catalogo (listas fechadas servidas pela API, fonte única); 19º verificador verifica-preferencias (bijeção 8/3/26 contra pg_enum); placares do verificador de acesso 19 / 24 / 13. Tela /treinos/preferencias nos dois modos (edição do aluno, leitura do profissional vinculado), botão Preferências em /treinos. Testes do web 74 -> 90; server 991. Achados do qa corrigidos no card: arrays de enum devolvidos como texto cru pelo driver (cast ::text[]), e o comando de limpeza recusando conta com preferências. Perda declarada: nenhuma.
RF-TREI-009.
Aceite (H1): Dado uma ficha criada por um profissional, Quando o aluno a duplica, Então nasce uma ficha nova com os mesmos exercícios, autoria do aluno e referência à origem, E a original permanece inalterada, E o aluno consegue editar a cópia.
Nota de escopo (gate {F3-09}, 2026-09-15): o card entrega RF-TREI-009 inteiro, as cinco cláusulas do H1, e nada além: a ação Duplicar individual no detalhe da ficha, ao lado de Pedir ajuste, a rota própria de duplicação com corpo vazio e o identificador da origem no caminho, a cópia nascendo com os mesmos exercícios na mesma ordem, autoria de quem duplicou e referência à ficha de origem, a exibição dessa referência no detalhe da cópia, e a cópia editável pelo novo autor pela rota de edição que {F3-03} já entregou. A cópia mantém o titular e muda só o autor: "para si", em RF-TREI-003, é sobre autoria e nunca sobre titularidade, o profissional não tem perfil de aluno para ser titular de ficha nenhuma, e o aluno que duplica já é o titular; trocar o titular criaria um caminho de mover ficha entre alunos que requisito nenhum pede. Título e descrição da cópia são idênticos aos da origem, sem sufixo e sem prefixo, porque quem distingue as duas na lista é o rótulo de autoria que já existe e a referência à origem no detalhe. A cópia nasce ativa e por isso conta em fichas_ativas, de modo que o aluno no teto do plano recebe a recusa de limite traduzida pelo mesmo tradutor único, sem segunda contagem e sem mensagem nova, e a ordem é sempre acesso antes de limite, com o aluno de acesso inativo recebendo a recusa de acesso e nunca a de limite. Uma ficha de origem inativa pode ser duplicada, porque RF-TREI-009 não condiciona ao estado e retomar uma ficha antiga é exatamente o caso de uso. A duplicação pré-checa o catálogo inteiro antes de escrever, no molde da edição, e a origem com exercício desativado desde então recusa nomeando a ordem, nunca com erro de banco. A referência à origem só aparece quando o leitor ainda pode ler a origem, e quem decide isso é a política de leitura pela junção e não um condicional na tela: origem fora de alcance simplesmente não é desenhada, sem rótulo de indisponível. O Administrador não duplica, e isso é medido e não escolhido: RF-TREI-009 nomeia Aluno e Profissional e não o nomeia, RN-009 lhe dá a edição e nunca a criação, e duplicar cria; a Matriz Ator por Capacidade do DRS ganhou nesta data a linha de duplicar, com ele em N, terceira quebra do padrão dele, com o motivo escrito. A dívida do comportamento ao excluir a origem, deixada por declarar na migração que criou a coluna de referência e nominada a este card, fecha aqui: a referência é anulada quando a ficha de origem é excluída em definitivo. As alternativas de recusa fariam a primeira cópia criada em produção revogar na prática o direito de exclusão em definitivo que RF-CORE-008 dá ao Administrador sobre a origem, e a alternativa em cascata apagaria fichas de outro autor e outro titular, contradizendo de frente a consequência escrita de ADR-006; anular perde a referência e nunca a cópia, e o momento da duplicação continua respondível pela trilha imutável de RF-CORE-009. A seleção em massa NÃO é deste card: a faixa com "Selecionar todas", contador e as ações Desativar e Duplicar em lote é RF-PAIN-010, tem caso {TA-81} e card dono {F7-16}, e as três Notas do desenho aprovado que a atribuíam a este card foram corrigidas nesta data. O que mais não entra, com dono nominal: duplicar plano alimentar é da Fase 4 ({F4-03}); notificar alguém de que uma ficha foi duplicada é {F6-05}, e requisito nenhum o pede hoje; listar as cópias pelo lado da origem não está em artefato nenhum e seria card novo; renomear no ato da duplicação, escolher outro titular, duplicar a própria ficha e duplicar pelo Administrador não existem; copiar sessões, histórico, execuções ou solicitações junto da ficha não acontece, porque o H1 cobra os mesmos exercícios e nada mais; geração assistida é Fase 8 ({F8-05} e {F8-12}); atalho de retomar, retomada e abandono são RF-TREI-010 e {F3-10}. Em todas, ausência é ausência: sem botão desabilitado, sem rótulo "em breve", sem banner. Nenhuma entidade e nenhuma rota nascem. Aritmética de placares, que só se movem no fechamento e medidos no dia: {ART-ENT-04} não muda, {ART-SCR-01} não muda, {ART-SCR-02} continua 1 de 4, {ART-FORM-01} não muda porque a cópia reusa os mesmos sete campos por item, {ART-CFG-01} continua com vinte e oito parâmetros, {ART-SM-03} continua [ ] porque duplicar não é transição de estado, {TA-06} não sobe porque nenhuma entidade nova nasce, {TA-10} continua 0 de 6; {TA-26} e {TA-32} fecham inteiros neste card, e {TB-05} continua [ ] por ser beta com aluno real. Placar que não muda também se declara, senão o leitor supõe que ninguém olhou. O título do card não é editado e o H1 do card não é editado.
Nota de fechamento (gate {F3-09}, 2026-09-15): merge e931e3b na main, gates plan, db, qa e review verdes (.claude/gates/{plan,db,qa,review}/modularfit-F3-09.gate), deploy provado em produção (https://fit.modulareasy.com), versões em produção durante o card c24d6cc, final e931e3b; migração 0030_duplicacao_da_ficha.sql aplicada (só troca a restrição de ficha_origem_id para ON DELETE SET NULL e reescreve o comentário; zero tabela, coluna, índice ou política), última em produção passa de 0029 para 0030. POST /api/fichas/:id/duplicar em transação única (leitura da origem pela RLS do leitor, 404 quando negada; guarda de papel em lista fechada aluno e profissional, administrador 403 nomeado; autor não duplica a própria, 403 nomeado; INSERT ... SELECT dos itens preservando ordem; cópia com o mesmo titular, autor = leitor, ficha_origem_id; 409 limite_do_plano pelo mecanismo existente; origem inalterada). podeDuplicar no ponto único de autoria, botão Duplicar no detalhe só ao não autor, linha Duplicada de com a origem. Testes web 96 -> 102; server 1004 com banco real (qa). Perda declarada: nenhuma. Achado do qa: nenhum bloqueante. {TA-26} e {TA-32} fecham inteiros; {TB-05} continua [ ], beta com aluno real. Placares: {ART-ENT-04}, {ART-SCR-01}, {ART-SCR-02}, {ART-FORM-01}, {ART-CFG-01} e {ART-SM-03} não mudam, nenhuma entidade ou rota nasce; {TA-06} não sobe.
Nota de correção (gate {F4-03}, Drift 5, 2026-09-30): a frase da Nota de escopo acima, "duplicar plano alimentar é da Fase 4 ({F4-03})", está corrigida: duplicar plano alimentar não tem requisito e não é de {F4-03} nem de card nenhum; se o dono quiser, é RF novo mais card. Nada do que {F3-09} entregou muda.
RF-TREI-010 e E.3.
O que implementar: atalho de retomar no painel e na lista de treinos enquanto houver sessão em andamento, retomada carregando do banco e não da memória da tela, e passagem automática a abandonada pelo prazo configurado.
Nota de fronteira (gate {F3-05}, Drift 6, 2026-09-14): a persistência da sessão e das séries no banco a cada registro saiu deste card e é servida por {F3-05}, por construção: {ART-ENT-04} atribui a série gravada a cada registro ao desenho da tabela, que nasce lá, e o H1 de RF-TREI-005 cobra duração e volume vindos do banco, conforme RN-004. Se a obrigação ficasse aqui, {F3-05} guardaria as séries na memória da tela e {F3-10} reescreveria o caminho de escrita inteiro: dois desenhos incompatíveis para a mesma coluna. Nenhum requisito é editado, RF-TREI-010 continua inteiro, e o que se corrige é a divisão de trabalho escrita neste card. Este card fica com o que é dele: o atalho, a retomada lendo do banco e o abandono pelo prazo configurado.
Nota de escopo (gate {F3-10}, Drifts 5 e 7, 2026-09-29): o que nasce neste card é o atalho nas duas telas, a regra de prazo e a correção do gatilho; a retomada pelo banco, entregue por {F3-05}, é LIGADA e provada aqui, não reconstruída. "Último registro" inclui a navegação sem série: herdado de {F3-05} (D-4, item 5) e mantido, porque mudar seria desfazer decisão de outro card.
Como verificar (teste primeiro): registrar duas séries, fechar o navegador, entrar em outro dispositivo e conferir o atalho nas duas telas e as séries carregadas; adiantar o relógio além do prazo e conferir que a sessão vira abandonada e some do atalho.
Aceite (H1): Dado um aluno que iniciou uma sessão e registrou duas séries, Quando fecha o navegador e volta em outro dispositivo, Então painel e lista de treinos exibem o atalho de retomar, E ao retomar as duas séries aparecem com o cronômetro seguindo do início original, E ao concluir o volume considera as séries anteriores mais as novas, E a mesma sessão sem registro além do prazo configurado passa a abandonada, some do atalho nas duas telas e não conta para sequência, calendário, conquistas nem progressão.
Nota de fechamento (gate {F3-10}, 2026-09-29): merge 047f3bc na main, gates plan, qa (PASS COM RESSALVAS, só de P2 depois da correção de contraste) e review (APPROVED) verdes (.claude/gates/{plan,qa,review}/modularfit-F3-10.gate), deploy provado em produção (https://fit.modulareasy.com), versões em produção durante o card 7b07990 e 3dba4d0, final 047f3bc; migração 0042_abandono_da_sessao_por_prazo.sql aplicada (só reescreve a função mfit.consolidar_sessao() e comentários; zero tabela, coluna, índice, política, gatilho ou concessão), última em produção passa de 0041 para 0042. O que entrou: o atalho de retomar no painel e em /treinos (só para o papel aluno); GET /api/sessoes/atual filtra por predicado e responde 204 à sessão vencida sem escrever; a passagem a abandonada tem escritor único e é acionada de forma preguiçosa nos quatro caminhos de escrita do titular (iniciar, registrar série, atualizar posição e encerrar); uma rotina de operação de hora em hora, em linha própria do agendador do servidor, cobre quem nunca volta; a tela de sessão mostra a mensagem do servidor no 409. O gatilho de encerramento passou a distinguir os dois eventos de E.3: a sessão com séries que passou do prazo termina abandonada, nunca concluída (Drift 1 do gate). Transições de E.3 da sessão servidas por caminho alcançável: 5 de 5 (retomar e prazo decorrido passam a servidas). O prazo é lido de sessao_abandono_horas (24, faixa 2 a 72), editável em /admin/parametros e valendo na próxima avaliação: provado com 24, 2, 4 e 24 sem reiniciar. Perda declarada: a sessão com séries que passou do prazo vira abandonada e suas séries deixam de contar (é o que RF-TREI-010 manda); o atalho some e GET /api/sessoes/atual devolve 204; o erro genérico de encerrar deu lugar à mensagem do servidor no 409; UPDATE direto para abandonada numa sessão com séries passa a produzir abandonada; o painel-marcador deixa de ter só Meu perfil. Para profissional, administrador e super administrador nada muda (provado pelo qa). Ressalvas P2 do qa, que não bloqueiam: P2-01, a mensagem do 409 aparece sem role=alert e não é anunciada por leitor de tela; P2-02, a 400 px o cronômetro quebra para a segunda linha e o aviso de falha fica alinhado à esquerda numa página centralizada; P2-03, a abandonada pela rotina grava fim e duração do momento do fechamento, não do último registro, o que é invisível hoje e deve ser avisado a quem consumir a métrica de {F7-15}. O P1 de contraste do atalho no painel claro (4,36 para 1) foi corrigido e reconferido em produção (4,96 para 1 no claro, 6,25 para 1 no escuro). Lacunas declaradas: a primeira execução automática da rotina horária ainda não foi observada (a linha do agendador foi instalada às 23h12 UTC de 29/09 e o primeiro disparo é às 00h00 UTC de 30/09; o comando é o mesmo que o qa rodou à mão, com contagem 1 e depois 0, idempotente), e o aluno de acesso inativo não foi exercitado. A Fase 3 fecha em código com este card: os dez cards {F3-01} a {F3-10} estão entregues. Da Definição de Pronto da Fase 3, a cláusula da sessão interrompida retomada sem perder série está provada em produção; {TA-79} fecha inteiro (as cláusulas de sequência, calendário e conquistas passaram aos casos donos {TA-55}, {TA-56} e {TA-57}), e o beta de execução continua [ ] por ser beta com aluno real. Placares: {ART-SM-03} fecha (5 de 5), {ART-ENT-04} permanece 6 de 6, {ART-CFG-01} sobe de 33 para 34 de 46 com consumidor no ponto único; {ART-SCR-01} e {ART-SCR-02} não mudam, nenhuma rota de tela nasce.
Definição de Pronto da Fase 3: o aluno executa uma sessão completa no celular, uma sessão interrompida é retomada sem perder série, o histórico mostra a progressão e a autoria da ficha é respeitada em toda edição.
Fase 4, Dieta
Complementar ao núcleo: entra depois do treino inteiro estar no ar, e depende tecnicamente apenas da fase 2.
RF-DIET-001.
Nota (gate {AUD-07}, 2026-09-29): a lista nasce como linhas de mfit.lista_fechada_item (valor no CHECK de lista), nunca enum nem constante ({AUD-07}).
Aceite (H1): Dado um aluno na tela de refeições, Quando registra uma refeição com dois alimentos de cem e duzentas calorias, Então a refeição exibe trezentas calorias, E o mesmo vale para proteína, carboidrato e gordura, cada um igual à soma dos itens.
Nota (F1-10): entidade removível de RF-CORE-008. Ao criar a tabela, aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md): coluna desativado_em/desativado_por (R-2 do gate {F1-10}), a política FOR DELETE exigindo o papel administrador e o gatilho de remoção, e rodar o verificador de verificarPoliticas.ts antes do gate do qa.
Fechamento [2026-09-30]: entregue e em produção (merge 5e36145, migração 0043_refeicao.sql, https://fit.modulareasy.com, /api/estado na versão servida 5e36145 (= merge), gates plan (APPROVED com trava de drift, Drifts 1 a 5 republicados antes do código), qa (PASS COM RESSALVAS na rodada 1, PASS na rodada 2) e review (APPROVED na rodada 3) verdes). Telas /nutricao (mínima: navegador de data, refeições do dia com os quatro totais de cada uma, Registrar refeição, Desativar e o vazio do dia) e /nutricao/refeicoes/nova (estado de /nutricao, não tela nova). Duas tabelas, refeicao e refeicao_alimento; lista fechada tipo_refeicao com 8 itens (60 itens no total da tabela); duas linhas novas em mfit.parametro (44 no total). H1 provado em produção: 100 e 200 kcal dão 300 e as três somas de macronutriente, no POST e no GET do dia, e três itens com decimais somam exato. Fronteira de dado clínico: 1 de 13 entidades servidas. refeicao é a primeira: leitura e escrita negadas ao Administrador com 403 nomeado (dado criado antes, par positivo do titular no mesmo alvo devolvendo 200); no banco o Administrador lê 0 das 4 refeições existentes e o INSERT falha com 42501, e o Super Administrador lê com uma linha em acesso_administrativo por refeição. Placar de parâmetros: 52 pendentes de 98, C 51, universo 149 (decisão do Fiscal: a C a mais é o espelho web do teto de numeric(7,1), limite do tipo de coluna). Perda declarada: o Administrador perde o alcance a refeição, que nunca existiu, por decisão de RF-CORE-007; mfit.pode_ler_aluno de três argumentos passa a delegar com resultado idêntico; nada muda para aluno, profissional e Super Administrador no que já existia. Ressalvas do qa, as quatro fechadas na rodada 2: aviso geral do formulário que sobrava depois da correção, erro nomeado por campo, quantidade na lista do dia e vírgula sobrando em /admin/listas. Lacuna nominal: a comparação de pg_policies antes e depois só a sanidade efêmera do gate cobre, e não foi repetida em produção. Os quatro cartões contra a meta e o total do dia continuam sendo de {F4-04}.
RF-DIET-002.
Aceite (H1): Dado um aluno com meta de duas mil calorias, Quando o nutricionista vinculado altera para duas mil e quatrocentas, Então a tela diária passa a comparar o consumo com o valor novo, E a alteração fica na auditoria com o autor.
Nota (gate {F4-02}, Drift 2, 2026-09-29): o aceite medido é o H1 de RF-DIET-002 no DRS, com dois Nutricionistas e três vigências (2000, depois 2400, depois 2200), duas entradas de auditoria, cada uma com o autor correspondente, e o histórico preservando as três vigências sem apagar nenhuma; o H1 abreviado deste cartão (um Nutricionista, sem a cláusula do histórico) não é o critério, igual à decisão de {TA-34}. Título e H1 do cartão não se editam.
Fechamento [2026-09-30]: entregue e em produção (merge b569460, migração 0044, https://fit.modulareasy.com, /api/estado na versão servida b569460 (= merge), gates plan (APPROVED com trava de drift), qa (PASS COM RESSALVAS, reconferido na rodada 2) e review (APPROVED na rodada 2) verdes). Bloco "Metas do dia" em /nutricao nas duas pontas, formulário "Meta nutricional" (Editar metas) para o aluno titular e para o Nutricionista de vínculo ativo, e leitura do dia pelo Profissional vinculado em /nutricao?aluno=<id> (sem Registrar refeição e sem Desativar). Tabela meta_nutricional só de acréscimo (UPDATE e DELETE pela aplicação negados com 42501 no banco e 404 nas rotas PUT, PATCH e DELETE); mfit.meta_vigente(aluno, data) é o ponto único da meta do dia e mfit.tem_vinculo_ativo_na_especialidade é a primeira capacidade da Matriz aplicada por especialidade no banco. H1 do DRS provado em produção: dois Nutricionistas de vínculo ativo, 2000 (padrão), depois 2400, depois 2200; auditoria com exatamente 2 entradas, cada uma com o autor correspondente, e a vigência padrão sem entrada; histórico com as 3 vigências, sem apagar nenhuma; a meta de hoje não altera a de dias anteriores (ontem e 30 dias atrás seguem em 2000). Fronteira de dado clínico: 2 de 13 entidades servidas (meta_nutricional é a segunda e a primeira com autor): Administrador com 403 nomeado e 0 linhas no banco (INSERT negado com 42501), com o par positivo do titular no mesmo alvo. Personal, Médico e Fisio vinculados leem e não editam (GET 200, POST 403); Nutricionista sem vínculo ativo ou ex-vinculado: GET 404, POST 403. Placar de parâmetros: 52 de 98, C 53, universo 151 (ratificado pelo Fiscal; a C a mais é o espelho web da folga de fuso); metas_padrao ganha o primeiro consumidor. Perda declarada: Personal, Médico e Fisio leem e não editam meta; o Administrador nunca alcançou meta; o aluno existente guarda a vigência padrão que recebeu; Médico e Nutricionista continuam criando ficha de treino até {AUD-09}. Ressalvas do qa: R1 (cartão "Sem permissão para ver estes dados" no lugar de duas linhas soltas), R3 (botão "Histórico de metas" com 2,75rem de altura) e R4 (horário em vigências do mesmo dia) corrigidas e reconferidas; R2 confirmada pelo Fiscal como comportamento da lei (meta_nutricional é não sensível e sem categoria de consentimento em RF-CORE-010, então o servidor nega por vínculo e por papel, não por consentimento); na reconferência o Administrador é redirecionado a /admin em vez de ver o cartão, por desenho, e a API segue com 403 nomeado. Não entra aqui: a comparação do painel com a meta (consumido, restante e percentual) é de {F4-04}.
RF-DIET-003.
Nota (Inspeção, {AUD-11}, 2026-09-30): plano_alimentar e plano_refeicao nascem sob a categoria nutrição de RF-CORE-010: sensivel: true e categoriaConsentimento: 'nutricao' no registro de entidades, mfit.tem_consentimento(aluno_id, 'nutricao') no WITH CHECK de escrita e mfit.consentimento_visivel(aluno_id, 'nutricao') na leitura, pela quinta seção do gabarito, e o plano criado para aluno sem o consentimento é recusado com a tela de consentimento. Este card vem depois de {AUD-11} e de {AUD-10} na fila.
Nota (F1-16): ao criar a tabela da dimensão, o ramo novo entra no CASE de mfit.consumo_do_aluno (não mais em dentro_do_limite_do_aluno, que desde {F1-16} só decide); sem o ramo a função continua levantando exceção, de propósito, e a política nova nasce quebrando, nunca liberada em silêncio; e a tela /plano passa a exibir aquela dimensão sozinha, sem uma linha de código de front, não escrever uma segunda contagem para a tela.
Aceite (H1): Dado um nutricionista com vínculo ativo, Quando cria um plano com quatro refeições estruturadas, Então o plano aparece na tela do aluno na ordem de horário, E a edição por quem não é o autor é negada.
Nota (F1-10): entidade removível de RF-CORE-008. plano_alimentar já tem status; ao aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md), este card declara se inativo passa a usar o status existente ou se a tabela ganha desativado_em/desativado_por como as demais (R-2 do gate {F1-10}), além da política FOR DELETE exigindo o papel administrador e do gatilho de remoção, com o verificador de verificarPoliticas.ts rodando antes do gate do qa.
Nota (F1-15): a dimensão planos_alimentares_ativos é servida por esta tabela; ao criar a tabela, aplicar a seção "Limite de plano por dimensão" do gabarito e acrescentar o ramo 'planos_alimentares_ativos' ao CASE de mfit.dentro_do_limite_do_aluno, sem o ramo a função levanta exceção, de propósito, e a política nova nasce quebrando, nunca liberada em silêncio.
Nota de correção (gate {F4-03}, Drift 5, 2026-09-30): duplicar plano alimentar e pedir ajuste de plano NÃO nascem neste card, em forma nenhuma. Nenhum RF dá essas duas ações ao plano: RF-TREI-009 e RF-TREI-004 são de ficha, RN-009 diz "duplicar quando o tipo permitir" e nenhum requisito o permite para plano, e o H1 de RF-DIET-003 não as pede. O mockup desenha Duplicar e Pedir ajuste no detalhe do plano (tela 12b) e os dois botões não se constroem; "Gerar dieta com IA" e "Gerar com IA" são de {F8-06}. A Nota de {F3-09} que atribuía "duplicar plano alimentar" a este card está corrigida. Se o dono quiser as ações, é RF novo mais card.
Nota de correção (gate {F4-03}, Drift 7, 2026-09-30): a Nota (F1-15) acima está desatualizada quanto ao destino do ramo: desde {F1-16} o ramo 'planos_alimentares_ativos' mora em mfit.consumo_do_aluno (e mfit.dentro_do_limite_do_aluno só decide), como diz a Nota (F1-16). Vale a Nota (F1-16); da Nota (F1-15) permanece a aplicação da seção de limite de plano por dimensão do gabarito. Título e H1 do card não mudam.
Nota (Inspeção, 2026-09-30): Releitura obrigatória contra main com {AUD-11} mergeado antes de codar: o consentimento de nutrição (sétima categoria) passa a governar o plano alimentar.
Nota (gate {AUD-11}, drift 8, 2026-09-30): a releitura em uma passada, antes de codar, corrige quatro pontos deste card. (1) A migração passa a ser 0048, porque {AUD-11} usa 0046 e 0047. (2) A decisão aberta sobre nutrição como categoria de consentimento está decidida em {AUD-11}: as conjunções do gabarito (mfit.tem_consentimento(aluno_id, 'nutricao') no WITH CHECK, mfit.consentimento_visivel(aluno_id, 'nutricao') na leitura) entram na própria 0048. (3) plano_alimentar declara o seu leitorUnico no registro de entidades (hoje provisoriamente acesso/leituraSensivel.ts), conforme a Nota de {ART-AUD-02}, e o leitor de módulo chama registrarSeAdministrativo. (4) A inserção de plano_alimentar exige também mfit.consentimento_visivel(aluno_id, 'nutricao'), porque a restrição por especialidade governa a escrita (drift 7 de {AUD-11}): o profissional fora da restrição não grava.
Nota (Inspeção, gate {AUD-10}, drift 8, 2026-09-30): a migração 0048 é de {AUD-10} (0048_modelos_de_email.sql), e não deste card: onde a Nota do gate de {AUD-11} acima diz que a migração passa a ser a 0048, leia a primeira migração livre depois de {AUD-12}, que numera a 0049 (ou a 0049 e a 0050 se precisar de ADD VALUE). O gate de {F4-03} é relido em uma passada antes de codar.
Nota (Inspeção, gate {AUD-12}, drift 4, 2026-09-30): a migração deste card passa a ser a 0052 ({AUD-12} ocupa a 0050 e a 0051); as Notas anteriores que citam a 0048 e a 0049 ficam superadas.
Nota de correção (Inspeção, gate {F4-03}, Drift 10, 2026-09-30): a Nota de {AUD-11} acima, que diz que plano_alimentar e plano_refeicao nascem com sensivel: true no registro de entidades, fica corrigida: plano_refeicao herda o consentimento do plano pai (como refeicao_alimento herda de refeicao) e fica fora do registro de entidades sensíveis; a entidade sensível é plano_alimentar. A contagem de entidades sensíveis continua 10.
Fechamento [2026-09-30]: entregue e em produção (merge 2f5c560, repositório wladimirdjr/modularfit, migração 0052, https://fit.modulareasy.com, /api/estado na versão 2f5c560; o qa provou em produção nas versões 0cf4faa e 5fbaf4b; gates plan (APPROVED COM TRAVA DE DRIFT, releitura contra 4e5ed26 aprovada para codar), db (PASS), qa (PASS COM RESSALVAS, reconferência na rodada 2 PASS) e review (APPROVED na rodada 2) verdes). Tela /nutricao/planos para o aluno e para o Profissional vinculado, com os estados /nutricao/planos/:id, /nutricao/planos/novo e /nutricao/planos/:id/editar; botão Planos alimentares em /nutricao. H1 provado em produção: o Nutricionista de vínculo ativo cria quatro refeições fora de ordem (20:00, 12:30, 06:30, 16:00) e o aluno as vê na ordem 06:30, 12:30, 16:00, 20:00; as calorias saem somadas pelo banco, iguais à soma independente (1148; 1030,5 pela interface, com vírgula); a edição por aluno, Personal e outro Nutricionista é negada com 403 nomeado e título intacto, e o autor edita com atualizado_por gravado. Desativar com confirmação nominal, 409 ao repetir, reativar; aluno sem Nutricionista cria o próprio plano (201), com Nutricionista ativo recebe 403 nomeado e a tela não oferece Criar plano; limite do plano Gratuito aplicado (segundo ativo 409 nomeando o plano que amplia, /plano mostra 1 de 1). Consentimento de nutrição ({AUD-11}): sem consentimento o aluno não cria, não edita nem desativa (403 consentimento_necessario) e continua lendo, e o Nutricionista vinculado sem consentimento recebe 403 categoria_nao_compartilhada. Fronteira de dado clínico: 3 de 13 entidades servidas; Administrador com 403 nomeado nas seis rotas, 0 linhas lidas de 7 no banco e escrita recusada, com os pares positivos no mesmo alvo. Placares: consentimento por entidade 3 de 10, leitor único 3 de 10, capacidade por especialidade 12 de 12, verifica-parametros inalterado (51 de 98, C 54, universo 152, 33 parâmetros, 7 listas). Exceção FILHAS_COM_EXCLUSAO_NA_EDICAO ratificada pelo Fiscal: o DELETE de plano_refeicao na edição não é a remoção de entidade de RF-CORE-008 (precedente de ficha_item), com a condição cumprida: a trilha grava refeicao_removida com o conteúdo anterior (nome, notas, horário e alimentos), autor e momento, na mesma transação do DELETE, e o qa provou o caso em produção na reconferência (5fbaf4b: Jantar removido, uma linha de trilha, Café e Almoço mantidos). Perda declarada: o aluno com Nutricionista de vínculo ativo não cria plano próprio (o plano que criou antes continua dele); a linha Planos alimentares de /plano deixa de ser não medida; Administrador nunca alcançou plano; o Super Administrador edita plano de terceiro pela API, sem tela; o texto do 403 consentimento_necessario passa a citar o plano alimentar; a ordem das refeições não é renumerada no PATCH. Ressalvas do qa: P1-1 (a trilha sem o valor anterior) e P2-2 (tipografia do nome da refeição) corrigidas e reconferidas; P2-1 segue, porque o autor fora da restrição do aluno recebe 404 e não 403 nomeado em editar e desativar (escrita barrada, plano intacto); P2-3 é de fora do card; lacuna declarada: a tela /admin/auditoria do Super Administrador não foi percorrida e o ramo "Super edita plano" foi lido no código, não exercitado. Não entra aqui: duplicar plano e pedir ajuste (sem requisito), notificação ao aluno ({F6-05}), geração com IA ({F8-06}), acompanhamento por período ({F4-05}) e cartões de consumo contra a meta ({F4-04}).
Nota (Inspeção, {AUD-11}, 2026-09-30): a leitura do profissional vinculado passa por mfit.consentimento_visivel(aluno_id, 'nutricao'): sem o consentimento de nutrição do aluno, ou com a categoria restrita a outra especialidade, o painel e o acompanhamento lhe são negados, e o titular continua lendo.
RF-DIET-004.
Aceite (H1): Dado um aluno com meta de duas mil calorias e mil e duzentas registradas, Quando abre o painel diário, Então vê mil e duzentas consumidas, oitocentas restantes e sessenta por cento, E num dia sem registro os quatro totais aparecem em zero, sem erro.
Nota (gate {F4-01}, Drift 3, 2026-09-29): completa a tela que {F4-01} abre. {F4-01} entrega /nutricao em versão mínima (navegador de data, lista das refeições do dia com os quatro totais de cada uma, Registrar refeição, Desativar e o vazio do dia); este card acrescenta os quatro cartões contra a meta e o total do dia.
Nota (gate {F4-02}, Drift 4, 2026-09-29): consome mfit.meta_vigente(aluno, data), o ponto único que {F4-02} cria para a meta do dia, e nunca reescreve a regra; fecha a cláusula de comparação de {TA-34} (consumido, restante e percentual contra a meta do dia). {F4-02} entrega só o bloco "Metas do dia" (os quatro valores da meta, o autor e o histórico) nas duas pontas; os quatro cartões contra a meta seguem sendo deste card.
Fechamento [2026-09-30]: entregue e em produção (merge a6624e9, repositório wladimirdjr/modularfit, migração 0053, https://fit.modulareasy.com; o qa provou em produção nas versões 86449c2 e 3f98363, e 3f98363 é o código do merge; gates plan (APPROVED COM TRAVA DE DRIFT), db (PASS), qa (PASS COM RESSALVAS, reconferência em 3f98363 PASS) e review (APPROVED na rodada 3) verdes). Em /nutricao, o bloco "Metas do dia" ganha no topo os quatro cartões contra a meta (calorias, proteína, carboidrato e gordura, cada um com o consumido sobre a meta, o restante e o percentual) e o total do dia (refeições e kcal); depois de Desativar uma refeição o bloco recarrega sem o aluno recarregar a página. Regra única da comparação: mfit.comparacao_com_meta(consumido, meta) (migração 0053, função pura, IMMUTABLE, sem SECURITY DEFINER) devolve o restante com uma casa e o percentual inteiro, meio para cima e sem teto; meta nula, zero ou negativa devolve nulo e a tela diz "Sem meta definida", sem barra (RN-004). A soma do dia é feita pelo banco, só com refeições ativas (totaisDoDia, no leitor único de refeição); dia sem registro dá zero nos quatro. O painel é o campo painel de GET e POST /api/metas, de corpos iguais. Funções do schema de 57 para 58, políticas 77 e parâmetros 33, inalterados. H1 provado em produção ({TA-36}): meta 2000 e 1200 registradas mostram 1.200 consumidas, 800 restantes e 60%; o dia sem registro mostra os quatro totais em zero, "Nenhuma refeição registrada" e "Dia sem registro", sem erro e com o console limpo. Casos medidos: abaixo da meta, 1463,7 de 2200 dá 736,3 restantes e 67%; acima, 2300 de 2000 mostra "300 kcal acima da meta · 115%" com a barra cheia; igual, 230 de 230 dá 0 e 100%; proteína com meta 0 mostra "Sem meta definida"; desativar uma refeição leva 1200 a 700 e 60% a 35% na hora; a navegação por data usa a meta daquele dia. Quarta cláusula de {TA-34} provada: com um Nutricionista trocando a meta para 2400 e depois outro para 2200, o painel compara sempre com a mais recente, e {TA-34} fecha 4 de 4. Acesso: o painel herda a leitura de GET /api/metas: o titular, o Profissional de vínculo ativo com a categoria de nutrição visível (sem Registrar nem Desativar) e o Super Administrador, que grava uma linha de acesso administrativo por refeição somada; sem a categoria visível o profissional recebe 403 categoria_nao_compartilhada e o Administrador, 403 nomeado. Verificador da fronteira endurecido (D-8): a chamada positiva passou a ser obrigatória em exclusaoNaEdicaoValida e nos critérios de Super Administrador e de administração; NOT e = false reprovam, e a forma viva da migração 0032 (grupo OR dentro de AND) continua aprovada, com os decoys e os controles das políticas reais nos testes. PATCH concorrente de plano (D-9): o segundo recebe 409 conflito_de_edicao em vez de 403, provado 6 de 6 em produção, e a negação legítima segue 403. Placares medidos: fronteira de dado clínico 3 de 13, consentimento por entidade 3 de 10, leitor único 3 de 10, capacidade por especialidade 12 de 12, verifica-parametros inalterado em 51 de 98, C 54, universo 152, 33 parâmetros e 7 listas. Perda declarada: a barra do painel deixa o degradê da marca (decisão do qa, por causa do contraste mínimo de 3:1 contra a trilha); Personal, Médico e Fisioterapeuta de vínculo ativo com a categoria visível passam a ver a conta do consumo contra a meta, de dois dados que já liam; o corpo de GET e POST /api/metas cresce com painel; o Super Administrador passa a gravar mais linhas de trilha ao ler o dia; o verificador reprova mais formas; e o segundo PATCH concorrente deixa de receber 403. Ressalvas do qa: P2-1 ("/ meta" quebrando de linha no cartão de calorias), P2-2 (valor ARIA acima do máximo) e P2-3 (contraste da barra no tema claro) corrigidas e reconferidas em 3f98363; lacunas declaradas, o D-9 não foi rodado contra o código anterior e o D-8 não foi exercitado pelo qa (coberto pelo arnês e pelo review). O link "Painel" do topo com área de toque menor que a do padrão vem de {F4-01} e é corrigido em {F4-05}. Não entra aqui: dia com meta atingida e médias por período ({F4-05}), bloco do painel geral ({F7-06}), tolerância e cor de alerta, que não existem.
Nota (Inspeção, {AUD-11}, 2026-09-30): a leitura do profissional vinculado passa por mfit.consentimento_visivel(aluno_id, 'nutricao'): sem o consentimento de nutrição do aluno, ou com a categoria restrita a outra especialidade, o painel e o acompanhamento lhe são negados, e o titular continua lendo.
RF-DIET-005.
Aceite (H1): Dado um nutricionista com vínculo ativo em um aluno com trinta dias de registros, Quando abre o acompanhamento com período de trinta dias, Então vê a média diária de calorias e de cada macronutriente e o número de dias em que a meta foi atingida, E o mesmo acompanhamento para aluno sem vínculo ativo é negado.
Nota (gate {F4-02}, Drift 4, 2026-09-29): a leitura do dia pelo Profissional (/nutricao?aluno=<id>, sem Registrar refeição nem Desativar) e o bloco "Metas do dia" nasceram em {F4-02}; este card é o acompanhamento por período (média diária e dias na meta) e lê a meta de cada dia do mesmo ponto único, mfit.meta_vigente, sem reescrever a regra.
Nota (gate {F4-04}, Drift 2, 2026-09-30): mfit.comparacao_com_meta, migração 0053, é a regra única da comparação do consumo com a meta; {F4-05} a consome e define por cima dela o que é meta atingida. A razão: se a tela calculasse o percentual e o acompanhamento calculasse em SQL, um dia que a tela mostra "100 por cento" poderia não contar como atingido.
Nota (Inspeção, gate {F4-05}, Drifts 1 e 2, 2026-09-30): a regra do acompanhamento (o que é dia com meta atingida, média só sobre dias com registro, período que termina ontem, janelas de 7 e 30 dias) está publicada como Nota em RF-DIET-005 do DRS, e o parâmetro nutricao_meta_tolerancia_pct (10, faixa 1 a 50) em L.2 e em {ART-CFG-01}. Migração 0054, a entregar neste card, sem marcar entrega: a função mfit.meta_atingida(consumido, meta, tolerância), que consome mfit.comparacao_com_meta ({F4-04}) e decide o dia, e o parâmetro novo em mfit.parametro, que passa de 33 para 34 linhas. O código que lê o parâmetro entra no mesmo deploy da migração, porque a trava de cardinalidade da carga faz o processo antigo recusar subir com 34 linhas. Drifts 3 a 6 estão como Notas em RF-DIET-005, no Apêndice de telas do DRS, em {F5-07} e em {ART-DASH-03}.
Fechamento [2026-10-01]: entregue e em produção (merge f609a9c, repositório wladimirdjr/modularfit, migrações 0054 e 0055, https://fit.modulareasy.com; o qa provou em produção em de85d1c e reconferiu em 1b0002c; gates plan (APPROVED), db (PASS), qa (PASS COM RESSALVAS na rodada 1, PASS na reconferência) e review (CHANGES_REQUESTED só de texto do CHANGELOG, corrigido em 60cbdce) verdes). A tela /nutricao/acompanhamento?aluno= (estado de /nutricao, com o botão "Acompanhamento" no cabeçalho do Profissional) e a rota GET /api/acompanhamento-nutricional entregam o acompanhamento por período de 7 ou 30 dias: média diária de calorias e de cada macronutriente, dias com registro, dias na meta e dias sem meta, e a lista dos dias. Regra única, no banco: mfit.meta_atingida(consumido, meta, tolerância) (migração 0054, função pura, IMMUTABLE, sem SECURITY DEFINER) decide o dia sobre mfit.comparacao_com_meta; a meta é a vigente DO DIA (nunca a de hoje), a média é só sobre dias com registro, o dia sem registro nunca conta na meta, refeição desativada fica fora e o período termina ontem na tela. Tolerância editável: o parâmetro nutricao_meta_tolerancia_pct (10, faixa 1 a 50) é a trigésima quarta linha de mfit.parametro e vale na próxima leitura, sem reiniciar. H1 provado em produção ({TA-37}): aluno com 30 dias de registros, 28 com registro e 19 na meta, médias 2197,8 de calorias, 137,4, 274,7 e 73,3 de macronutrientes, iguais à soma independente; o aluno sem vínculo recebe 404, e o vinculado sem consentimento de nutrição recebe 403 categoria_nao_compartilhada. Bordas da tolerância com meta 2000: 1789 fora, 1790 dentro, 2209 dentro, 2210 fora; tolerância trocada para 5 dá 11 dias e para 25 dá 28, 0 e 51 recusados com 400, e o valor 10 foi restaurado e conferido. Decisão única de leitura de nutrição (D-4): as três rotas antigas (/api/refeicoes, /api/metas, /api/planos-alimentares) e a nova passam a decidir a leitura num lugar só, e as três antigas mantêm status, erro e campo idênticos ao pai (a6624e9) em 210 de 210 combinações. LinkDoTopo: os links do topo de dezesseis telas viram um componente único com área de toque de 2,75rem em 375 de largura, destino e texto iguais, e .mf-plano-voltar deixa de existir. Placares medidos: funções do schema de 58 para 59, políticas 77 (inalterado), parâmetros de 33 para 34, última migração 0055 (só de COMMENT, porque os textos de mfit.parametro e da política de alteração ainda diziam 33), verifica-parametros 51 de 98, C 56, universo 154, 7 listas; fronteira de dado clínico 3 de 13, consentimento por entidade 3 de 10, leitor único 3 de 10 e capacidade por especialidade 12 de 12, inalterados. Perda declarada: o Super Administrador, ao ler o acompanhamento, grava uma linha acesso_administrativo por refeição do período e por vigência lida (28 refeições mais 2 vigências no caso provado), trilha maior por desenho; Personal, Médico e Fisioterapeuta com a categoria de nutrição visível passam a ver médias e dias na meta, conta sobre dados que já liam; o Aluno não ganha tela de acompanhamento (a API o serve, a tela redireciona para /painel); a linha do topo de dezesseis telas fica um pouco mais alta; o processo antigo não sobe contra a tabela de 34 parâmetros. Ressalvas do qa: P2-1 (cartão de 404 com título e texto que se contradiziam) e P2-2 (etiqueta de situação quebrando de linha a 400 de largura) corrigidas e reconferidas em 1b0002c (48 de 48 combinações de tela, tema e largura); P2-3 (trilha do Super por refeição) previsto e declarado. Lacuna declarada: o cenário de dias sem meta vigente (dias_sem_meta maior que zero) não foi exercitado pelo qa, porque a vigência padrão cobre todo dia anterior; está coberto pelo arnês do executor. Fecha a Fase 4: {F4-01} a {F4-05} entregues. Não entra aqui: o bloco de peso e composição ({F5-07}), o painel do Nutricionista e a aderência média ({F7-08}), e o P2-1 do {F4-03}, fechado sem card por decisão do Fiscal (o 404 fica, porque um 403 nomeado revelaria a existência do plano a quem a restrição do aluno exclui).
Definição de Pronto da Fase 4: o aluno registra e enxerga o dia contra a meta, e o nutricionista prescreve e acompanha sem tocar em dado de aluno de outro profissional.
Fase 5, Saúde clínica e evolução
Complementar ao núcleo, e a fase de maior consequência jurídica: nenhuma tela clínica sobe sem o consentimento da fase 1 e sem o aviso de natureza não clínica de RF-CORE-012 no topo dela.
RF-SAUD-001 e ADR-007.
O que implementar: esquema único com tronco comum e seções por especialidade; um registro por ocasião de coleta, com data, autor e especialidade do autor; lista das anamneses do aluno em ordem decrescente de data; pré-preenchimento editável do tronco a partir da anamnese anterior.
Como verificar (teste primeiro): registrar uma anamnese, registrar outra meses depois por outra especialidade alterando campos do tronco, e conferir no banco que existem dois registros e que o primeiro manteve os valores originais.
Aceite (H1): Dado um aluno com uma anamnese registrada por um Personal Trainer, Quando um Médico vinculado preenche uma anamnese nova, Então o formulário abre com o tronco pré-preenchido pela anterior e com as seções médicas, E ao salvar existem exatamente duas anamneses, cada uma com a própria data, autor e especialidade, E a anterior permanece intacta inclusive nos campos alterados na nova, E a lista exibe as duas em ordem decrescente de data.
Nota (F1-09): entidade sensível de RN-001. A leitura desta entidade é servida pelo leitor único de registro sensível (server/src/acesso/leituraSensivel.ts), nunca por consulta própria da rota; rodar ops/verifica-leitura-sensivel.mjs antes do gate do qa, conforme a seção do gabarito em GABARITO-ACESSO-ALUNO.md.
Nota (F1-10): entidade removível de RF-CORE-008. Ao criar a tabela, aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md): coluna desativado_em/desativado_por (R-2 do gate {F1-10}), a política FOR DELETE exigindo o papel administrador e o gatilho de remoção, e rodar o verificador de verificarPoliticas.ts antes do gate do qa.
Nota (F1-12): entidade sensível de consentimento (RN-001). Ao criar a tabela, aplicar a quinta seção do gabarito (GABARITO-ACESSO-ALUNO.md, "Consentimento por categoria"): marcar categoriaConsentimento: 'anamnese' na entrada dela em ENTIDADES_E1; a política de leitura em conjunção com mfit.consentimento_visivel(aluno_id, 'anamnese'), além de mfit.pode_ler_aluno; o WITH CHECK de INSERT e de UPDATE em conjunção com mfit.tem_consentimento(aluno_id, 'anamnese'); o gatilho AFTER INSERT chamando mfit.auditar_criacao('aluno_id'); e rodar o verificador de {F1-12} antes do gate do qa, subindo o placar N de 6 no Plano de Testes (TA-10).
Nota [F1-18]: a tela nasce com <AvisoNaoClinico ponto="telas_clinicas" /> no topo, consumindo o texto vigente da tabela; não escreva o parágrafo no componente. O décimo terceiro verificador (ops/verifica-aviso-nao-clinico.mjs) reprova se esta tela existir sem ele, e o placar de RF-CORE-012 sobe de 2 para 3 e para 4 conforme elas nascerem.
Nota (Inspeção, gate {F5-01}, Drifts 1 a 5, 7 e 9, 2026-10-01): publicados antes do código. (1) A ficha dos 46 campos, com tipo, obrigatoriedade e teto, está no Apêndice: Catálogo de formulários do DRS e em {ART-FORM-04}: todos opcionais, e o envio exige ao menos um campo (422 anamnese_vazia). (2) Dois tetos de texto editáveis, anamnese_texto_curto_max_caracteres (200, faixa 80 a 500) e anamnese_texto_longo_max_caracteres (2000, faixa 500 a 10000), em L.2 e em {ART-CFG-01}: mfit.parametro passa de 34 para 36 linhas. (3) Quem preenche o quê, Nota em RF-SAUD-001: o profissional, o tronco e somente a seção da própria especialidade, com vínculo ativo nela; o aluno, o tronco e as seções das especialidades sem vínculo ativo. (4) A anamnese não se edita: correção é nova coleta, e o autor e o Super Administrador só desativam (Notas em RF-SAUD-001 e em RN-009); o banco concede atualização somente em desativado_em e desativado_por. (5) A ficha de anamnese em {ART-ENT-06} está corrigida: data é o momento da coleta (padrão do banco), criado_em sai, entram desativado_em e desativado_por, índice (aluno_id, data decrescente). (7) A tela é a casca de /saude, que serve as quatro telas clínicas. (9) Correção da Nota F1-09 acima: o leitor único de registro sensível (leituraSensivel.ts) lê um registro por id e emite o endereço, mas não lista nem escreve, e esta entidade precisa listar, gravar e desativar; no molde de {AUD-11} (nutrição), o leitor único da anamnese é o módulo anamnese/anamnese.ts, que lê, lista, grava e desativa e chama o registro de leitura administrativa de leituraSensivel.ts em toda leitura, nunca uma cópia. Ficam fora deste card: edição de conteúdo, explicação ({F5-10}), avaliação, exame, prescrição, reabilitação, anexo, pré-preenchimento pelo perfil, 360 do aluno, exportação e /admin/emails. Migração 0056, a entregar neste card, sem marcar entrega: a tabela mfit.anamnese, as quatro políticas e os dois parâmetros; tabelas de 31 para 32, políticas de 77 para 81, funções inalteradas em 59, parâmetros de 34 para 36, listas inalteradas em 7. O código que lê os dois parâmetros entra no mesmo deploy da migração, porque a trava de cardinalidade da carga faz o processo antigo recusar subir com 36 linhas.
Fechamento [2026-10-01]: entregue e em produção (merge 9cb16f3, repositório wladimirdjr/modularfit, migração 0056, https://fit.modulareasy.com (/api/estado na versão 9cb16f3); o qa provou em produção na versão 5b42dd0, e as correções dos achados P2-1 e P2-4 entraram em 25b0cc6, com testes 9 de 9 no efêmero e deploy em produção; gates plan (APPROVED com trava de drift, drifts publicados antes do código), db (PASS), qa (PASS COM RESSALVAS, limpeza de produção concluída depois) e review (APPROVED, 0 Critical, 0 Important, 6 Minor) verdes). Entregue: a tabela mfit.anamnese com quatro políticas (anamnese_leitura, anamnese_insercao, anamnese_atualizacao, anamnese_exclusao) e quatro gatilhos, sem função nova; GRANT UPDATE somente em desativado_em e desativado_por, de modo que nenhum campo de conteúdo aceita atualização (nova coleta nunca altera a anterior); as rotas GET /api/anamneses?aluno=, GET /api/anamneses/:id, GET /api/anamneses/base?aluno=, POST /api/anamneses e POST /api/anamneses/:id/desativar; as telas /saude (casca com o aviso não clínico no topo e a aba Anamneses), /saude/anamneses/nova e /saude/anamneses/:id; o contrato único de 46 campos (tronco de 20 mais as quatro seções de 5, 5, 8 e 8) com espelho na web; os dois tetos de texto editáveis (anamnese_texto_curto_max_caracteres 200 e anamnese_texto_longo_max_caracteres 2000), de modo que mfit.parametro vai de 34 para 36 linhas; o link "Saúde" em /alunos e no painel do aluno; e o ponto único de decisão de leitura e de tradução da negação generalizado por categoria (nutrição passou a chamá-lo com os mesmos textos). H1 provado em produção ({TA-38}): a matriz de quatro profissionais por quatro seções dá 12 negativos (403 secao_fora_da_permissao, contagem de linhas inalterada) e 4 positivos (201); o aluno sem vínculo grava o tronco e as quatro seções, com Personal, Médico e Nutricionista ativos grava só a seção funcional e leva 403 nas três outras; especialidade_autor, autor e data vêm da sessão e do banco (corpo com qualquer um deles dá 400 campo_desconhecido); o detalhe de uma anamnese fica byte a byte igual depois de três novas coletas, e o tronco-base passa a ser o da próxima ativa quando a mais recente é desativada; sem consentimento o aluno recebe 403 consentimento_necessario e o profissional 403 categoria_nao_compartilhada, e o par positivo concede e grava 201; restrita ao Médico, Personal e Nutricionista levam 403; revogada, o titular e o Super leem e ninguém grava; o Administrador recebe 403 nomeado nas cinco rotas, sem alterar o banco; o não vinculado recebe 404; desativar vale para o autor e para o Super, a segunda vez dá 409 e o não autor recebe 403 nomeado; PATCH, PUT, reativar e ativar dão 404; validações de formato e de teto (vazia 422, só brancos 422, caractere NUL 400, dor 11 e -1 400, 200 e 2000 caracteres aceitos, 201 e 2001 recusados). Trilha: criar grava em anamneses, desativar grava 3, alterar e excluir gravam 0, e o Super lendo a lista deixa uma linha acesso_administrativo por anamnese devolvida (6 de 6) e uma no detalhe. Placares medidos: tabelas de 31 para 32, políticas de 77 para 81, funções inalteradas em 59, parâmetros de 34 para 36, listas inalteradas em 7 (db-ops, 107 casos por modo, 107 ok, 0 falhas); verifica-parametros 51 de 98, C 57, universo 155; capacidade por especialidade 14 de 14; leitor único 4 de 10; suíte do servidor 1440 testes, 1360 passam, 0 falham, 80 pulados, e web 283 de 283. Perda declarada: o autor não corrige erro de digitação sem nova coleta (desativa e coleta de novo), porque a anamnese não se edita (ADR-007); o Super Administrador personificando consegue gravar anamnese como o aluno (201, autor igual ao aluno, trilha com o personificador), no mesmo desenho das demais escritas, e a decisão de proibir é do Fiscal (P2-3); o botão Desativar existe só no detalhe do autor e não no cartão da lista, divergência declarada do desenho D-10 (o cartão é um link); data não é travada pela política do banco, só omitida pelo servidor, e travá-la é decisão do Fiscal em migração futura; o processo antigo não sobe contra a tabela de 36 parâmetros; o Super lendo a lista grava uma linha de trilha por anamnese. Ressalvas do qa: P2-1 (grupo "saúde" sem rótulo em /admin/parametros, aparecia "SAUDE") e P2-4 (a limpeza de contas de teste recusava quando o Super desativou uma anamnese) corrigidos em 25b0cc6; P2-2 (JSON malformado devolve 422 anamnese_vazia em vez de 400) fica como está, igual à nutrição; P2-3 vai ao Fiscal. Limpeza: limpar-contas-de-teste.mjs --prefixo qa.f501 --aplicar apagou as 9 contas de teste, aritmética fechada, 0 resíduo em 40 verificações. Lacunas declaradas: contraste medido só a olho, sem ferramenta; a tela não foi comparada pixel a pixel com a tela 14 do mockup; o Super personificando não foi exercitado pela interface (só pela API); 429 e carga não testados; a edição dos dois tetos em /admin/parametros não foi exercitada em produção (os valores ficaram como estão); o review aceitou 6 Minor (entre eles o CASE por papel de SQL_CAPACIDADE como segunda cópia da política, coberto por teste de coerência). Abre a Fase 5. Não entra aqui: o botão Explicar ({F5-10}), edição de conteúdo, avaliação, exame, prescrição e reabilitação ({F5-02} a {F5-06}), anexo, pré-preenchimento pelo perfil, 360 do aluno ({F7}) e exportação ({F8-09}).
RF-SAUD-002.
Nota (gate {AUD-07}, 2026-09-29): a lista nasce como linhas de mfit.lista_fechada_item (valor no CHECK de lista), nunca enum nem constante ({AUD-07}).
O que implementar: as seis seções para as quatro especialidades e para o aluno, incluindo a antropometria pelo Nutricionista, com as quatro dobras cutâneas do protocolo padrão (tríceps, suprailíaca, abdominal e coxa, em milímetros, lista de pontos vinda da configuração), o teste de flexibilidade na seção neuromuscular, o teste de resistência na aeróbica e o histórico de lesões, medicamentos e estilo de vida na anamnese resumida.
Aceite (H1): Dado um aluno com uma avaliação anterior, Quando um profissional vinculado de qualquer uma das quatro especialidades registra uma nova, Então a comparação exibe cada métrica presente nas duas com valor anterior, atual e variação, E métricas presentes em apenas uma aparecem sem variação calculada, E o Nutricionista vinculado registra a avaliação com a antropometria completa, sem recusa por especialidade.
Nota (F1-09): entidade sensível de RN-001. A leitura desta entidade é servida pelo leitor único de registro sensível (server/src/acesso/leituraSensivel.ts), nunca por consulta própria da rota; rodar ops/verifica-leitura-sensivel.mjs antes do gate do qa, conforme a seção do gabarito em GABARITO-ACESSO-ALUNO.md.
Nota (F1-10): entidade removível de RF-CORE-008. Ao criar a tabela, aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md): coluna desativado_em/desativado_por (R-2 do gate {F1-10}), a política FOR DELETE exigindo o papel administrador e o gatilho de remoção, e rodar o verificador de verificarPoliticas.ts antes do gate do qa.
Nota (F1-12): entidade sensível de consentimento (RN-001). Ao criar a tabela, aplicar a quinta seção do gabarito (GABARITO-ACESSO-ALUNO.md, "Consentimento por categoria"): marcar categoriaConsentimento: 'avaliacao_fisica' na entrada dela em ENTIDADES_E1; a política de leitura em conjunção com mfit.consentimento_visivel(aluno_id, 'avaliacao_fisica'), além de mfit.pode_ler_aluno; o WITH CHECK de INSERT e de UPDATE em conjunção com mfit.tem_consentimento(aluno_id, 'avaliacao_fisica'); o gatilho AFTER INSERT chamando mfit.auditar_criacao('aluno_id'); e rodar o verificador de {F1-12} antes do gate do qa, subindo o placar N de 6 no Plano de Testes (TA-10).
Nota (Inspeção, gate {F5-02}, Drifts 1 a 9, 2026-10-01): publicados antes do código. (1) A ficha dos campos da avaliação, com tipo, faixa e teto, está no Apêndice: Catálogo de formulários do DRS e em {ART-FORM-05}: todos opcionais, o envio exige ao menos um campo (422 avaliacao_vazia), e a seção cardiovascular ganha idade_anos, insumo da frequência cardíaca máxima de {F5-03}. (2) Dois tetos de texto editáveis, avaliacao_texto_curto_max_caracteres (200, faixa 80 a 500) e avaliacao_texto_longo_max_caracteres (2000, faixa 500 a 10000), em L.2 e em {ART-CFG-01}: mfit.parametro passa de 36 para 38 linhas. (3) A avaliação não se edita: correção é nova avaliação, e o autor e o Super Administrador só desativam (Notas em RF-SAUD-002 e em RN-009); o banco concede atualização somente em desativado_em e desativado_por. (4) Regra da comparação, Nota em RF-SAUD-002: contra a ativa anterior ou outra ativa escolhida, métrica de teste só com o mesmo teste, ausente nunca vale zero. (5) A ficha de avaliacao_fisica em {ART-ENT-06} e em E.1 está corrigida: sai cálculos derivados; entram especialidade_autor, dobras_pontos, dobras_mm, desativado_em e desativado_por; data é gravada pelo banco e travada na política. (6) A lista ponto_dobra_cutanea nasce neste card, com quatro itens (Tríceps, Suprailíaca, Abdominal e Coxa): as listas fechadas com consumidor passam de sete para oito. (7) A comparação nasce como bloco do detalhe, e a aba Avaliações nasce dentro da casca de /saude; o mockup não tem tela de comparação e traz campos fora da ficha (o DRS vence). (8) Correção da Nota F1-09 acima: o leitor único de registro sensível (leituraSensivel.ts) lê um registro por id, mas não lista nem escreve, e esta entidade precisa listar, gravar e desativar; o leitor único da avaliação é o módulo avaliacao/avaliacao.ts, que lê, lista, grava e desativa e chama o registro de leitura administrativa em toda leitura, no molde da anamnese. (9) Fronteira com os vizinhos: Notas em {F5-03} (cálculos feitos na leitura) e em {F5-07} (a medida corporal não é escrita aqui). Migração deste card: 0057_avaliacao_fisica.sql, a seguinte à 0056 de {F5-01}: a tabela mfit.avaliacao_fisica com quatro políticas e gatilhos, a lista ponto_dobra_cutanea com quatro itens e os dois parâmetros; tabelas do schema de 32 para 33, políticas de 81 para 85, parâmetros de 36 para 38 e listas de 7 para 8, a medir no fechamento e não a projetar. Ficam fora deste card: cálculos derivados ({F5-03}), edição de conteúdo, explicação (RF-SAUD-010 não inclui a avaliação), pré-preenchimento por avaliação anterior, medida corporal e gráfico ({F5-07}), exame, prescrição e reabilitação ({F5-04} a {F5-06}), 360 do aluno e atalho do painel do profissional ({F7}) e exportação ({F8-09}). O cartão continua [ ].
Fechamento [2026-10-06]: entregue e em produção (merge 3ab8395, repositório wladimirdjr/modularfit, migrações 0057 e 0058 aplicadas em produção em 06/10, https://fit.modulareasy.com (/api/estado na versão 87ae8e0, a mesma árvore do merge; o deploy da main em 3ab8395 segue em curso); gates plan (APPROVED com trava de drift, drifts publicados antes do código), db (PASS, 0057 e 0058), qa (rodada 1 FAIL por um P1, reteste na versão 87ae8e0 PASS) e review (APPROVED, 0 Critical, 0 Important, 6 Minor) verdes). Entregue: a tabela mfit.avaliacao_fisica com quatro políticas (_leitura, _insercao, _atualizacao, _exclusao), cinco gatilhos (um de lista e quatro de auditoria) e sete CHECKs, sem função nova; GRANT UPDATE somente em desativado_em e desativado_por, de modo que nenhum campo de conteúdo aceita atualização (a avaliação não se edita, correção é nova avaliação); o INSERT exige data = now(); a lista ponto_dobra_cutanea (Tríceps, Suprailíaca, Abdominal e Coxa) em mfit.lista_fechada_item, editável em /admin/listas; os dois tetos de texto editáveis (avaliacao_texto_curto_max_caracteres 200 e avaliacao_texto_longo_max_caracteres 2000), de modo que mfit.parametro vai de 36 para 38 linhas e as listas fechadas com consumidor de sete para oito; as rotas de /api/avaliacoes (lista por aluno, detalhe com ?com= para escolher a avaliação de comparação, criar e desativar); as telas /saude/avaliacoes, /saude/avaliacoes/nova e /saude/avaliacoes/:id (aba Avaliações dentro da casca de /saude, com o aviso não clínico no topo); o contrato único de campos com espelho na web; o leitor único avaliacao/avaliacao.ts, que chama o registro de leitura administrativa em toda leitura; a comparação como função pura sobre as avaliações ativas (variação com sinal e unidade, métrica ausente aparece como "não informado" e nunca vale zero, métrica de teste só varia com o mesmo teste, avaliação desativada fica fora); e o tempo do teste aeróbico em minutos e segundos (mm:ss) no formulário, no detalhe e na comparação. Migração 0058 (aprovada pelo Fiscal sem card): troca só o CHECK anamnese_secoes_formato da anamnese pela forma estrita, que recusa {"medico":[]} (arranjo no lugar de objeto), com aborto nomeando a contagem de linhas violadoras (produção tinha 0 anamneses); provada 19 de 19 casos e com rollback ensaiado, e no qa o POST com {"medico":[]} dá 400. H1 provado em produção ({TA-39}): cada uma das quatro especialidades registra com a especialidade_autor correta (201), o Nutricionista com a antropometria completa e quatro dobras, o aluno registra com especialidade_autor nula (com quatro vínculos e sem vínculo); a comparação mostra anterior, atual e variação com sinal e unidade (peso -1,5 kg, dobra -1,0 mm), métrica presente em só uma avaliação sem variação, o mesmo teste normalizado varia e testes diferentes aparecem como "testes diferentes"; consentimento ausente, restrito e revogado negam como esperado (403 categoria_nao_compartilhada para o Personal quando restrito ao Médico, titular e Super leem a revogada); o Administrador recebe 403 nomeado nas rotas e o Super lê o mesmo detalhe; o não vinculado recebe 404; desativar vale para o autor e o Super (segunda vez 409, não autor que lê 403, PATCH, PUT, DELETE e reativar 404); validações de forma e de teto (campo desconhecido no corpo, NUL, dobra inexistente, 11 testes, idade 121, peso 501, 200 e 2000 caracteres aceitos, 201 e 2001 recusados). Trilha: criar grava em avaliacoes, alterar 0, e o Super lendo a lista deixa uma linha acesso_administrativo por avaliação devolvida (duas na comparação). Placares medidos: db-ops no efêmero, 133 casos por modo, 133 ok, 0 falhas, tabelas de 32 para 33, políticas de 81 para 85, funções inalteradas em 59, parâmetros de 36 para 38, listas de 7 para 8; produção conferida pelo qa em 38 parâmetros e 8 listas em /admin/listas; verificar:acesso capacidade por especialidade 16 de 16; leitor único 5 de 10; verifica-parametros 51 de 98, C 58, universo 156; suíte do servidor 1475 testes, 1397 passam, 0 falham, 78 pulados, e web 306 de 306. Perda declarada: a avaliação não se corrige, só se refaz (RN-009); o autor de vínculo encerrado lê o detalhe pelo identificador e não encontra a avaliação na lista (decisão do Fiscal: mantém); o Super ao desativar gera duas linhas acesso_administrativo (leitura prévia e resposta); JSON malformado em POST /api/avaliacoes devolve 422 avaliacao_vazia em vez de 400, mesmo padrão já aceito na anamnese, e a decisão do Fiscal leva o ajuste para {F5-03}; o placeholder pendente: F5-02 de avaliacao_fisica nas suítes genéricas de remoção e acesso administrativo segue (a cobertura real está em avaliacao.test.ts, e a anamnese tem o mesmo placeholder). Ressalvas do qa: P1-1 (a tabela de comparação mostrava o tempo em segundos) corrigido em 87ae8e0 e confirmado no reteste (12:34, 11:50 e -0:44 em claro e escuro); P2 "Comparar com" (opções idênticas no mesmo dia) corrigido, com rótulo "1a do dia" e a medida; P2 do degrau do cartão "Mais recente" corrigido, 0 degrau em 6 larguras; P2-5 (lista de anamneses a 768 de largura, etiqueta do cartão "Mais recente" quebra e desce a data 27 px) fica aberto como achado novo, sem bloqueio, e não se provou se já existia. Limpeza: limpar-contas-de-teste.mjs na rodada 1 apagou 11 contas e na do reteste 5 contas (--prefixo qa.f502r), aritmética fechada e 0 resíduo em 43 verificações nas duas, usuários restantes 4 (três de topo mais aluno.teste). Lacunas declaradas: item de lista desativado pelo Super não exercitado em produção (coberto em S-8 do db), Super personificando não exercitado, contraste de fundo em gradiente não medido pelo instrumento (olhado, legível), 429 e carga não testados; o review aceitou 6 Minor. Não entra aqui: cálculos derivados (IMC, relação cintura quadril e frequência cardíaca máxima, {F5-03}), exame, prescrição e reabilitação ({F5-04} a {F5-06}), medida corporal ({F5-07}), o botão Explicar ({F5-10}), edição de conteúdo e exportação ({F8-09}).
RF-SAUD-003 e RN-004.
Como verificar (teste primeiro): teste unitário por fórmula, com valor conhecido e caso de borda de campo ausente.
Aceite (H1): Dado um aluno com oitenta quilogramas, um metro e oitenta, cintura noventa, quadril cem e trinta anos, Quando a avaliação é salva, Então o índice de massa corporal exibido é 24,7, a relação cintura quadril é 0,90 e a frequência cardíaca máxima é 190, E as faixas de intensidade são calculadas sobre esse valor.
Nota (Inspeção, gate {F5-02}, Drift 9, 2026-10-01): os cálculos são uma função pura no contrato da avaliação, aplicada na LEITURA (detalhe, lista e comparação), sobre peso_kg, altura_cm, cintura_cm, quadril_cm e idade_anos, que {F5-02} já grava; nenhuma coluna e nenhuma migração para guardar cálculo (RN-004, "calculados, não digitados"). Campo ausente nunca vira zero: sem a altura, o índice de massa corporal não é exibido. Os cálculos entram na comparação como métricas, pela mesma regra de RF-SAUD-002.
Nota (Inspeção, gate {F5-03}, Drifts 1 a 6 e P2 visual, 2026-10-06): publicados antes do código; nenhuma marcação muda e o H1 não muda. (1) Onde: o cálculo aparece no detalhe e na comparação; a lista fica fora, porque o RF exige exibir a fórmula e ela não cabe no cartão. Isto substitui "detalhe, lista e comparação" da Nota do Drift 9 acima. (2) Constantes: a frequência cardíaca máxima como 220 menos a idade, o índice de massa corporal, a relação cintura quadril e as cinco zonas (50 a 60, 60 a 70, 70 a 80, 80 a 90 e 90 a 100 por cento da frequência máxima) são constantes de literatura com fonte citada (Fox, 1971; Quetelet e OMS; OMS, 2008; ACSM), e não parâmetro: mudar é mudar RN-004. Uma linha C nomeada no verificador de parâmetros; nenhum parametro() novo, nenhuma coluna e nenhuma migração. (3) Comparação: índice de massa corporal (uma casa, kg/m²), relação cintura quadril (duas casas) e frequência cardíaca máxima (zero casas, bpm) entram ao fim, na seção "Cálculos", pela regra de RF-SAUD-002 (e); as zonas não entram (função da frequência máxima, dez linhas redundantes); as linhas antigas mantêm ordem e chave. (4) Não exibido: o número some e a linha diz "não calculado: informe a altura"; nunca zero. (5) Arredondamento: meio para cima sobre a representação decimal, e não toFixed; divisor zero ou ausente devolve nulo. (6) Corpo inválido: 400 corpo_invalido para JSON malformado ou corpo que não é objeto, em um ponto único novo, server/src/http/corpoJson.ts, adotado em POST /api/avaliacoes e POST /api/anamneses; nutrição não muda (JSON malformado lá segue 422 campos_invalidos); medido no gate: 36 leituras de corpo em 23 arquivos, 9 em 4 arquivos já respondem 400 por try/catch próprio; o restante é o card {AUD-13}. (7) P2 visual (alinhamento das listas clínicas): .mf-clin-lista passa a usar subgrid, com o item da lista e o cartão em grid-template-rows: subgrid e uma linha explícita por parte (cabeçalho, data, resumo opcional, rodapé), de modo que cartões vizinhos alinham cabeçalho, data e rodapé na mesma linha de grade; o cabeçalho é uma grade 1fr auto sem quebra, com a etiqueta na primeira linha do título; sem suporte a subgrid vale a regra atual; só em e rem, zero px. Prova: degrau zero medido em 360, 768, 1024, 1280, 1440 e 1920, nos temas claro e escuro, nas duas listas (anamnese e avaliação, com o título longo "Prática esportiva" e etiqueta).
Perda declarada ({F5-03}): o cliente que manda JSON inválido ou corpo que não é objeto às duas rotas recebia 422 *_vazia e passa a receber 400 (os testes de anamnese e de avaliação que esperavam 422 passam a esperar 400, decidido); no cartão da lista, a etiqueta deixa de quebrar para baixo do título e passa a ficar ao lado, inclusive em tela estreita. Nada mais: o detalhe e a comparação só ganham campos, e nutrição e as demais leituras de corpo ficam intocadas.
Fechamento [2026-10-06]: entregue e em produção (merge 08b35ed, repositório wladimirdjr/modularfit, sem migração, https://fit.modulareasy.com (a mesma árvore já provada em produção como 208a883; o deploy da main em 08b35ed segue em curso); gates plan (APPROVED com 7 drifts, 1 a 4 publicados antes do código), review (rodada 1 CHANGES REQUESTED por um Important, corrigido, APPROVED) e qa (rodada 1 PASS, 0 P0, 0 P1, 0 P2, 2 P3) verdes). Entregue: a função pura calcularDerivados (server/src/avaliacao/calculos.ts), aplicada na leitura, sem coluna e sem migração: índice de massa corporal, relação cintura quadril, frequência cardíaca máxima (220 menos a idade) e as cinco zonas de intensidade, cada um com valor, unidade, casas e fórmula visível; o bloco "Cálculos" no detalhe da avaliação (valor, unidade e fórmula, zonas em tabela que empilha em tela estreita, aviso não clínico no topo); três linhas novas ao fim da comparação (calculo.imc, calculo.rcq, calculo.fc_max, seção "Cálculos"), com as 14 linhas antigas na mesma ordem e chave e as zonas fora; o ponto único server/src/http/corpoJson.ts, que faz POST /api/avaliacoes e POST /api/anamneses responderem 400 corpo_invalido a JSON malformado, corpo vazio, null, lista, texto e número; e o subgrid nas listas clínicas (.mf-clin-lista), com a etiqueta "Mais recente" ao lado do título. H1 provado em produção ({TA-40}): 80 kg, 180 cm, cintura 90, quadril 100 e 30 anos dão índice de massa corporal 24,7, relação cintura quadril 0,90 e frequência cardíaca máxima 190, com as zonas 95 a 114, 114 a 133, 133 a 152, 152 a 171 e 171 a 190 sobre esse valor, na API e na tela; 48 kg e 160 cm dão 18,8 e 28,8 kg e 160 cm dão 11,3 (o meio para cima, que o review achou errado em ponto flutuante e foi corrigido para a conta em centímetros); idade 33 dá 187; sem insumo o valor é nulo e a linha diz "Não calculado: informe ..." (peso e altura, cintura e quadril, idade), nunca zero. A lista de avaliações não traz cálculo. 400 provado: nas duas rotas, os seis corpos inválidos dão 400 com a contagem de registros inalterada, {} segue 422 (avaliacao_vazia e anamnese_vazia), o Administrador recebe 403 antes do 400 e nutrição (/api/refeicoes) segue 422 campos_invalidos. Subgrid provado: degrau 0 em título, data, legenda, rodapé e altura dentro da fileira, nas listas de anamneses e de avaliações, em 360, 768, 1024, 1280, 1440 e 1920, nos temas claro e escuro (24 combinações, 0 achados, controle negativo detectado), sem rolagem horizontal. Placares medidos: suíte do servidor na rodada do review 1491 testes, 1413 passam, 0 falham, 78 pulados (os mesmos de {F5-02}), com a correção do Important acrescentando dois testes (48 kg e 160 cm, 32 kg e 160 cm; arquivo de cálculos 13 de 13); web 315 de 315; verificar:parametros 51 de 98 inalterado, universo 156 para 157, exceção C 58 para 59 (a linha das constantes de literatura); verificar:acesso e verificar:leitura-sensivel (5 de 10) verdes. Perda declarada: o cliente que manda corpo inválido às duas rotas recebia 422 *_vazia e passa a receber 400 (decidido, D6), e no cartão da lista a etiqueta fica ao lado do título e, em 768, o título longo quebra em duas linhas no cartão com etiqueta e o vizinho reserva a linha em branco (consequência do subgrid, P3). Ressalvas: os 2 P3 do qa (o espaço sob o título curto em 768; os rótulos "Zona", "Intensidade" e "Batimentos" literais no JSX e o tipo corpo_invalido triplicado, que {AUD-13} reúne). Lacunas declaradas: o Super personificando e o Chrome com cabeça não foram exercitados (Edge headless via playwright-core). Limpeza: 4 contas, 7 avaliações físicas e 3 anamneses de teste apagadas pelo script, aritmética fechada e 0 resíduo em 43 verificações. O resto do 400 é {AUD-13}, que segue [ ] e entra antes de {F5-04}. Não entra aqui: classificação do índice de massa corporal (o produto não é clínico), cálculo na lista, as zonas na comparação, exame, prescrição e reabilitação ({F5-04} a {F5-06}), medida corporal e gráfico ({F5-07}).
RF-SAUD-004 e ADR-009.
Nota (gate {AUD-07}, 2026-09-29): a lista nasce como linhas de mfit.lista_fechada_item (valor no CHECK de lista), nunca enum nem constante ({AUD-07}).
Nota (R-14, {F2-02}): o middleware de cabeçalhos, a guarda de tamanho, a detecção de formato pelos bytes e a ordem "objeto antes da linha" nascem em {F2-02}. Este card não constrói uma segunda via de arquivo: escreve no repositório privado reusando aquele desenho, e acrescenta o que é dele (consentimento, endereço assinado na leitura, auditoria de leitura sensível).
Aceite (H1): Dado um aluno que consentiu a categoria exame, Quando envia um exame com anexo de até vinte megabytes, Então o exame aparece na lista com tipo, nome e data e o anexo abre por endereço assinado, E arquivo acima do limite ou de extensão fora da lista é recusado com a mensagem do campo.
Nota (F1-09): entidade sensível de RN-001. A leitura desta entidade é servida pelo leitor único de registro sensível (server/src/acesso/leituraSensivel.ts), nunca por consulta própria da rota; rodar ops/verifica-leitura-sensivel.mjs antes do gate do qa, conforme a seção do gabarito em GABARITO-ACESSO-ALUNO.md.
Nota (F1-10): entidade removível de RF-CORE-008. Ao criar a tabela, aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md): coluna desativado_em/desativado_por (R-2 do gate {F1-10}), a política FOR DELETE exigindo o papel administrador e o gatilho de remoção, e rodar o verificador de verificarPoliticas.ts antes do gate do qa.
Nota (F1-12): entidade sensível de consentimento (RN-001). Ao criar a tabela, aplicar a quinta seção do gabarito (GABARITO-ACESSO-ALUNO.md, "Consentimento por categoria"): marcar categoriaConsentimento: 'exame' na entrada dela em ENTIDADES_E1; a política de leitura em conjunção com mfit.consentimento_visivel(aluno_id, 'exame'), além de mfit.pode_ler_aluno; o WITH CHECK de INSERT e de UPDATE em conjunção com mfit.tem_consentimento(aluno_id, 'exame'); o gatilho AFTER INSERT chamando mfit.auditar_criacao('aluno_id'); e rodar o verificador de {F1-12} antes do gate do qa, subindo o placar N de 6 no Plano de Testes (TA-10).
Nota (F1-16): ao criar a tabela da dimensão de armazenamento de anexos, o ramo novo entra no CASE de mfit.consumo_do_aluno (não mais em dentro_do_limite_do_aluno, que desde {F1-16} só decide); sem o ramo a função continua levantando exceção, de propósito, e a política nova nasce quebrando, nunca liberada em silêncio; e a tela /plano passa a exibir aquela dimensão sozinha, sem uma linha de código de front, não escrever uma segunda contagem para a tela.
Nota [F1-18]: a tela nasce com <AvisoNaoClinico ponto="telas_clinicas" /> no topo, consumindo o texto vigente da tabela; não escreva o parágrafo no componente. O décimo terceiro verificador (ops/verifica-aviso-nao-clinico.mjs) reprova se esta tela existir sem ele, e o placar de RF-CORE-012 sobe de 2 para 3 e para 4 conforme elas nascerem.
Nota (Inspeção, gate {F5-01}, Drift 7, 2026-10-01): a Nota F1-18 acima passa a dizer: a aba desta tela nasce DENTRO da casca de /saude, criada em {F5-01}, que já traz <AvisoNaoClinico ponto="telas_clinicas" /> no topo e serve as quatro telas clínicas; não nasce rota de tela clínica fora da casca, e o verificador reprova aba clínica fora dela. O placar de RF-CORE-012 sobe de 2 para 3 com {F5-01}, e o quarto ponto é o pacote de exportação ({F8-09}); as abas de {F5-04} a {F5-06} não sobem o placar, porque o ponto 3 é uma tela só.
Nota (Inspeção, gate {F5-04}, Drifts 1 a 11 e migração 0059, 2026-10-06): publicados antes do código; nenhuma marcação muda e o H1 não muda. (1) Fronteira com {F5-09}: este card só EMITE o endereço assinado no detalhe, pelo ponto que já existe (emitirEnderecoArquivoSensivel); não toca assinar.ts, privado.ts, a faixa de 1 a 60 minutos de url_assinada_validade (o "até quinze minutos" do H1 de {F5-09} é daquele card) nem a separação entre mídia e exame. (2) Limite de envio: o limite de corpo de 20 megabytes do servidor web barra um arquivo de exatamente 20 MiB mais o envelope do envio com um 413 em HTML, e o H1 falharia na borda; a faixa de anexo_tamanho_max vai a 50, então o limite sobe para 51 megabytes (validado antes da recarga, recarregado e nunca reiniciado). Condição ratificada: o limite de 51 megabytes vale SÓ no bloco do servidor de fit.modulareasy.com, nunca no arquivo global do servidor web, e a faixa publicada de anexo_tamanho_max (1 a 50 megabytes) não pode passar do limite desse bloco: subir a faixa exige subir o limite antes e republicar a faixa. (3) Desativar não apaga o objeto: o exame desativado segue legível ao titular e ao Super Administrador e o anexo segue contando na cota; o objeto só é apagado na exclusão definitiva (hoje só a limpeza, sem rota de exclusão clínica, precedente da anamnese), e quem garante é o verificador de órfãos disco contra banco no repositório privado. (4) Diretório vazio: o defeito do diretório que sobrava vazio é corrigido AQUI, no mecanismo comum de remoção: remover um objeto apaga o diretório pai quando ele fica vazio, nunca acima da raiz do repositório; o exercício ganha a mesma limpeza junto. (5) Aviso aos Médicos: a notificação aos Médicos vinculados (Apêndice de formulários, {ART-WF-07}) NÃO é deste card: fica em {F6-05}, no precedente do Drift 6 de {F4-03}; o H1 de {F5-04} e o caso {TA-41} não a pedem. (6) Placares parciais, sem marcar: com {F5-04}, {TA-85} fica em 3 de 4 (a aba Exames nasce dentro da casca de /saude, que já contava; Drift 7 de {F5-01}); sobem {TA-10} de 3 de 7 para 4 de 7, {TA-07} de 5 de 10 para 6 de 10, a capacidade por especialidade de 16 para 18 e o leitor único de registro sensível de 5 de 10 para 6 de 10, a medir no fechamento. (7) O exame não se edita: a atualização só desativa, e só o autor ou o Super Administrador (como em {F5-02}); notas em RF-SAUD-004 e RN-009 do DRS. (8) Data: a data do exame é do usuário, não futura com a folga de fuso já existente no servidor, e o momento do registro é gravado pelo banco e exigido igual a agora na política (Apêndice de formulários). (9) Tetos de texto como parâmetros (precedente da refeição): exame_nome_max_caracteres 160 (faixa de 80 a 500), exame_resumo_max_caracteres 4000 (faixa de 500 a 10000) e exame_observacoes_max_caracteres 2000 (faixa de 500 a 10000), e o mínimo de 3 caracteres do nome é regra fixa; mfit.parametro passa de 38 para 41 linhas (L.2, {ART-CFG-01}). (10) Cota de anexos: ramo novo de anexos_megabytes em mfit.consumo_do_aluno (soma de anexo_bytes, megabytes arredondados para cima; a foto entra em {F5-08}); a política usa a forma genérica (consumo anterior menor que o limite) e a soma com o anexo novo é decidida pelo servidor ANTES de gravar o objeto, com a mensagem de limite existente. (11) Tipo pelos bytes e pela extensão declarada: os dois na lista e da mesma família (jpg é jpeg); documento por %PDF-; texto só se for UTF-8 válido, sem byte nulo e sem controle além de tabulação e quebra de linha; a lista tipo_exame nasce com cinco itens e as listas fechadas com consumidor passam de oito para nove. Migração deste card: 0059_exame.sql, a seguinte à 0058: a tabela mfit.exame (id, aluno_id, autor_id, especialidade_autor nula quando o autor é o aluno, tipo, nome, data_exame, resumo, observacoes, anexo_chave, anexo_bytes e anexo_formato juntos ou nenhum, criado_em, desativado_em e desativado_por); leitura por pode_ler_aluno(aluno_id, autor_id, false, 'exame') em conjunção com consentimento_visivel; inserção com tem_consentimento e vínculo ativo de Médico (literal) ou titular; atualização concedida somente em desativado_em e desativado_por; exclusão só do Super Administrador; cinco gatilhos (um de lista e quatro de auditoria); nenhuma função nova; a lista tipo_exame e os três parâmetros. Tabelas do schema de 33 para 34, parâmetros de 38 para 41 e listas de 8 para 9, a medir no fechamento e não a projetar; a trava de cardinalidade da carga passa a 41 no mesmo deploy da migração (migração e código novo entram no mesmo passo, sem janela). Ficam fora deste card: o endereço assinado com validade curta e a separação mídia x exame ({F5-09}), o aviso aos Médicos ({F6-05}), a explicação em linguagem simples ({F5-10}), a foto de progresso e a cota dela ({F5-08}) e a exportação ({F8-09}). Perda declarada: um envio acima de 20 megabytes e até 51 chega à aplicação, que o recusa pela guarda de tamanho, em vez de receber o 413 do servidor web; no exercício, a remoção passa a apagar também o diretório vazio (ninguém o lia); nada mais, porque a mídia só troca a origem das assinaturas. O cartão continua [ ].
Fechamento [2026-10-06]: entregue e em produção (merge b078e76, repositório wladimirdjr/modularfit, migração 0059_exame.sql aplicada em produção em 06/10, limite de envio de 51 megabytes no bloco do servidor de https://fit.modulareasy.com, só nele (a mesma árvore já provada em produção como bde96b3; o deploy da main em b078e76 segue em curso); gates plan (APPROVED com 11 drifts, D1 a D11 ratificados pelo Fiscal, 1, 3, 5 e 7 publicados antes do código), db (PASS), review (rodada 1 APPROVED, 0 Critical, 0 Important, 6 Minor) e qa (rodada 1 PASS, 0 P0, 0 P1, 0 P2, 3 P3) verdes). Entregue: a tabela mfit.exame (migração 0059: quatro políticas, cinco gatilhos, atualização concedida somente em desativado_em e desativado_por, exclusão só do Super Administrador, nenhuma função nova); o ramo anexos_megabytes em consumo_do_aluno (soma de anexo_bytes de toda linha do titular, ativa ou desativada, em megabytes arredondados para cima, os três ramos antigos intactos); os três tetos de texto como parâmetros editáveis com as faixas publicadas (exame_nome_max_caracteres 160, faixa de 80 a 500; exame_resumo_max_caracteres 4000 e exame_observacoes_max_caracteres 2000, ambos de 500 a 10000); a lista tipo_exame (Laboratorial, Imagem, Cardiológico, Funcional e Outro); a escrita única de arquivos (arquivos/escrita.ts, pública e privada, no lugar de escritaPublico.ts) com a ordem 401, Administrador 403, guarda de Content-Length, corpo, consentimento, vínculo, cota, objeto e linha, e remoção do objeto se a linha falhar; o tipo pelos bytes e pela extensão declarada (formatoPelosBytes); o ponto único do módulo de exame (exame/exame.ts), que grava uma só trilha administrativa por detalhe com anexo e não põe endereço na lista; e a aba Exames dentro da casca de /saude, com o aviso não clínico no topo, sem px. Contagens medidas: tabelas do schema de 33 para 34, políticas de 85 para 89, funções inalteradas em 59, parâmetros de 38 para 41, listas fechadas de 8 para 9, rotas de corpo 54 = 37 JSON + 2 multipart + 15 sem corpo. H1 provado em produção ({TA-41}): PDF de exatamente 20 MiB aceito (201) e aberto pelo endereço assinado com 20971520 bytes; 20 MiB mais um byte recusado com a mensagem do campo (JSON anexo_grande_demais, com a faixa de 1 a 50 e o limite vigente, e não o 413 em HTML do servidor web); .exe, .svg, .html, PNG renomeado para .pdf, arquivo vazio, sem extensão e texto com byte nulo recusados, com zero objeto no disco. O endereço abre sem cookie; assinatura adulterada, expiração estendida e endereço vencido (medido de verdade) dão 403. Sem consentimento, com consentimento revogado ou restrito a outra especialidade, e para o Personal Trainer e o Administrador, não grava (com os pares positivos em 201); o Médico sem vínculo recebe 404. Desativar não apaga o objeto: o arquivo segue no disco, abre e a cota não cai. Cota do plano Gratuito: cinco envios de 20 MiB fecham 100 de 100 e o sexto recebe 409 com a mensagem de limite. Tetos 160, 4000 e 2000 aceitos e mais um caractere recusado. No exercício, remover a imagem e apagar o exercício fazem a pasta sumir (D4). 72 combinações de 6 larguras, 2 temas e 6 telas sem rolagem horizontal. Limpeza das contas de teste provada: zero resíduo, verificador de órfãos 0, 0 e 0. Placares medidos: capacidade por especialidade 18 de 18; leitor único de registro sensível 6 de 10; consentimento por categoria 4 de 7; aviso não clínico segue 3 de 4 (Drift 7 de {F5-01}: a aba dentro da casca não é ponto novo); suíte do servidor na rodada do review 1597 testes, 1521 passam, 0 falham, 76 pulados (nenhum do exame); web 332 de 332; verificador de corpo 13 de 13 decoys. Perda declarada, confirmada: um envio de 20 a 51 megabytes chega à aplicação, que o recusa em JSON, em vez do 413 do servidor web; remover um objeto agora apaga também o diretório vazio (exercício e exame); nada mais. Lacunas declaradas: Fisioterapeuta e Nutricionista não foram exercitados (o cadastro de teste bateu em 429; coberto o Personal, mesma negação), a tela do 413 de servidor não foi exercitada (o cliente barra antes; o 413 foi provado por API) e o contraste por script não rodou nesta rodada. P3 do qa: a tela do arquivo de 20 MiB mais um byte diz "20 MB e o limite é 20 MB" (arredondamento); o alerta geral do formulário fica depois de corrigir; POST /api/exames sem o aluno dá 403 e não 400. Para os cards seguintes: o CHECK da extensão e a allowlist do repositório privado com Content-Disposition ficam em {F5-09} e {F5-08}, e um decoy do limite no INSERT da soma de grandeza em {F5-08}. Fora deste card, como declarado: o endereço de validade curta e a separação mídia x exame ({F5-09}), o aviso aos Médicos ({F6-05}), a explicação em linguagem simples ({F5-10}), a foto e a cota dela ({F5-08}) e a exportação ({F8-09}). O Beta correspondente segue [ ], porque depende de operador real. O cartão passa a [x].
RF-SAUD-005.
Aceite (H1): Dado um médico com vínculo ativo, Quando registra uma prescrição com diagnóstico, medicamentos, posologia e duração, Então ela aparece na tela do aluno com data e autor, E exibe o aviso de que não substitui o receituário oficial.
Nota (F1-09): entidade sensível de RN-001. A leitura desta entidade é servida pelo leitor único de registro sensível (server/src/acesso/leituraSensivel.ts), nunca por consulta própria da rota; rodar ops/verifica-leitura-sensivel.mjs antes do gate do qa, conforme a seção do gabarito em GABARITO-ACESSO-ALUNO.md.
Nota (F1-10): entidade removível de RF-CORE-008. Ao criar a tabela, aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md): coluna desativado_em/desativado_por (R-2 do gate {F1-10}), a política FOR DELETE exigindo o papel administrador e o gatilho de remoção, e rodar o verificador de verificarPoliticas.ts antes do gate do qa.
Nota (F1-12): entidade sensível de consentimento (RN-001). Ao criar a tabela, aplicar a quinta seção do gabarito (GABARITO-ACESSO-ALUNO.md, "Consentimento por categoria"): marcar categoriaConsentimento: 'prescricao' na entrada dela em ENTIDADES_E1; a política de leitura em conjunção com mfit.consentimento_visivel(aluno_id, 'prescricao'), além de mfit.pode_ler_aluno; o WITH CHECK de INSERT e de UPDATE em conjunção com mfit.tem_consentimento(aluno_id, 'prescricao'); o gatilho AFTER INSERT chamando mfit.auditar_criacao('aluno_id'); e rodar o verificador de {F1-12} antes do gate do qa, subindo o placar N de 6 no Plano de Testes (TA-10).
Nota [F1-18]: a tela nasce com <AvisoNaoClinico ponto="telas_clinicas" /> no topo, consumindo o texto vigente da tabela; não escreva o parágrafo no componente. O décimo terceiro verificador (ops/verifica-aviso-nao-clinico.mjs) reprova se esta tela existir sem ele, e o placar de RF-CORE-012 sobe de 2 para 3 e para 4 conforme elas nascerem.
Nota (Inspeção, gate {F5-01}, Drift 7, 2026-10-01): a Nota F1-18 acima passa a dizer: a aba desta tela nasce DENTRO da casca de /saude, criada em {F5-01}, que já traz <AvisoNaoClinico ponto="telas_clinicas" /> no topo e serve as quatro telas clínicas; não nasce rota de tela clínica fora da casca, e o verificador reprova aba clínica fora dela. O placar de RF-CORE-012 sobe de 2 para 3 com {F5-01}, e o quarto ponto é o pacote de exportação ({F8-09}); as abas de {F5-04} a {F5-06} não sobem o placar, porque o ponto 3 é uma tela só.
Nota (Inspeção, gate {F5-05}, Drifts 1 a 10 e migrações 0060 e 0061, 2026-10-06): publicados antes do código; nenhuma marcação muda e o H1 não muda. (1) A prescrição não se edita (Drift 1): só desativa, o autor ou o Super Administrador, como em {F5-01}, {F5-02} e {F5-04}; Nota em RN-009 do DRS. (2) Colunas (Drift 2): a tabela nasce COM observacoes e SEM explicacao_gerada, que é de {F5-10}. (3) Notificação (Drift 3): a notificação de prescrição criada fica em {F6-05}. (4) Aviso do receituário (Drift 4): texto versionado editável, chave nova aviso_receituario no ponto prescricao existente, publicada em /admin/textos e exibida pelo componente AvisoDoReceituario na lista, no formulário e no detalhe, com o texto vindo sempre da tabela e nunca do componente; o verificador conta 3 de 3. Não é RF-CORE-012: {TA-85} fica em 3 de 4 e este card não é dono do quarto ponto ({F8-09}). (5) Tetos (Drift 5): um só, prescricao_texto_longo_max_caracteres, 4000, faixa de 500 a 10000, para os quatro textos longos (diagnóstico, medicamentos, posologia e observações); o teto curto não nasce, porque a duração deixou de ser texto (Drift 7). mfit.parametro passa de 41 para 42 linhas, e não 43 (L.2, {ART-CFG-01}). (6) Data (Drift 6): data da prescrição não futura, com a folga de fuso espelhada do servidor (o literal na política é o mesmo AMPLITUDE_DE_FUSO_EM_DIAS, com verificador com decoy), e criado_em = now() exigido na política. (7) Duração estruturada (Drift 7, decisão da Inspeção): duracao_quantidade (inteiro maior que zero) e duracao_unidade (dias, semanas ou meses, restrição no banco, constante estrutural e não lista editável), ou uso_continuo; a vigência (data da prescrição mais a duração menos 1 dia; uso contínuo até desativar) alimenta {F7-09}; a posologia segue texto livre; nenhuma lista nova nasce e as listas com consumidor continuam 9. (8) {ART-FORM-08} (Drift 8) cobre dois formulários: este card entrega a metade prescrição e NÃO marca o cartão; {F5-06} o fecha. (9) Placares parciais, sem marcar (Drift 9): {TA-10} de 4 de 7 para 5 de 7; {TA-07} (leitor único de registro sensível) de 6 de 10 para 7 de 10; capacidade por especialidade do verificador de acesso de 18 para 20 entradas; rotas de corpo de 54 (37 JSON, 2 multipart e 15 sem corpo) para 56 (38, 2 e 16); universo do verificador de parâmetros de 160 para 162 (o teto longo e a constante da unidade, classificada C), a confirmar pelo verificador no fechamento; {TA-85} não se move; o teste D-6.3 passa a usar sessao_reabilitacao como tabela inexistente, porque prescricao passa a existir. Tudo a medir no fechamento, e não a projetar. (10) Escopo extra (Drift 10): (a) textoDeTamanhosDistintos no trecho espelhado de contratoExame: sobe a precisão (1 casa, 2 casas, KB, bytes) até tamanho e teto diferirem, e o 413 do servidor e a recusa do cliente usam a mesma; (b) hook comum web/src/saude/errosDoFormulario.ts: o alerta de campos é derivado e some quando não há erro, o alerta de outra origem fica até o próximo envio, adotado nos quatro formulários, e os três limparErroDe saem. O 403 de POST sem aluno NÃO muda: o auxiliar serve sete módulos, o desfecho depende do papel e a tela nunca envia sem aluno, e mudar mexeria em 14 rotas por um P3; a prescrição herda o 403, fixado em teste. Migrações deste card: 0060 contém SÓ ALTER TYPE mfit.chave_texto ADD VALUE 'aviso_receituario' (isolada; irreversível, e declarado); 0061_prescricao.sql cria a tabela mfit.prescricao (id, aluno_id, autor_id, especialidade_autor 'medico', diagnostico, medicamentos, posologia, duracao_quantidade, duracao_unidade, uso_continuo, data_prescricao, observacoes, criado_em, desativado_em e desativado_por); leitura por pode_ler_aluno(aluno_id, autor_id, false, 'prescricao') em conjunção com consentimento_visivel; inserção com autor igual à sessão, profissional, tem_vinculo_ativo_na_especialidade(aluno_id, 'medico') literal, tem_consentimento e data até hoje mais 1 dia; atualização concedida somente em desativado_em e desativado_por; exclusão só do Super Administrador; gatilhos de auditoria; o parâmetro e a semente do texto; nenhuma função nova. Ordem das recusas na rota de criação: 401, Administrador 403, corpo 400, aluno, consentimento, vínculo, campos, data e só então o INSERT; byte nulo recusado; trilha administrativa só do Super Administrador. Contagens a medir no fechamento, e não a projetar: tabelas do schema de 34 para 35, parâmetros de 41 para 42, listas fechadas com consumidor 9. Perda declarada: nos formulários de anamnese, avaliação e exame, o alerta de campos passa a sumir ao corrigir (antes ficava até o envio); a recusa de anexo grande muda de texto, mais precisa; nada mais. O cartão continua [ ].
Fechamento [2026-10-06]: entregue e em produção (merge de718a6, repositório wladimirdjr/modularfit, migrações 0060 e 0061 aplicadas em produção em 07/10 (UTC; noite de 06/10 em Brasília), https://fit.modulareasy.com (a mesma árvore já provada em produção como 9510270; o deploy da main em de718a6 segue em curso); gates plan (APPROVED com 10 drifts, D1 a D4 e D7 publicados antes do código, e a duração estruturada decidida pelo Fiscal), db (PASS), review (rodada 1 CHANGES REQUESTED por 1 Important, a tela /admin/textos sem a chave do aviso, corrigido em seguida; a revisão do ajuste de datas APPROVED) e qa (rodada 1 PASS, 0 P0, 0 P1; reteste bf90408 PASS; reteste 9510270 PASS) verdes). Entregue: a tabela mfit.prescricao (15 colunas, 8 restrições, 4 políticas, 4 gatilhos de auditoria, nenhuma função nova) e o módulo server/src/prescricao/ (leitor único), com GET /api/prescricoes, GET /api/prescricoes/:id, POST /api/prescricoes e POST /api/prescricoes/:id/desativar; não existe PUT, PATCH nem DELETE. Só o Médico com vínculo ativo grava (a decisão é do banco); Personal, Fisioterapeuta, Nutricionista, aluno e Administrador recebem 403, e o Super Administrador sem personificar também; o aluno só lê. A recusa por papel vem antes do consentimento. Duração estruturada: duracao_quantidade de 1 a 999, duracao_unidade em dias, semanas ou meses (constante estrutural com restrição no banco, não lista editável) ou uso_continuo explícito; vigente e vigente_ate saem da leitura, sem coluna. Aviso do receituário: texto versionado aviso_receituario no ponto prescricao, versão 1 semeada pela migração e editável em /admin/textos (a tela passou a oferecer a chave, com o rótulo "Aviso do receituário"), exibido pelo componente AvisoDoReceituario na lista, no formulário e no detalhe, sempre com o texto vindo da tabela; o qa publicou duas versões pela tela e voltou ao texto original, que é o vigente (as versões 2 e 3 ficam no histórico imutável). Contagens medidas: tabelas do schema de 34 para 35, políticas de 89 para 93, funções inalteradas em 59, parâmetros de 41 para 42 (só prescricao_texto_longo_max_caracteres nasce, 4000, faixa de 500 a 10000), listas fechadas com consumidor 9 (nada de lista muda); suíte do servidor 1653 testes, 1579 passam, 0 falham, 74 pulados; verificadores: acesso (capacidade 20 de 20, consentimento 7 de 10, fronteira 7 de 13), leitura sensível 7 de 10, parâmetros (universo 165, 67 classificados C, 98 editáveis), corpo (56 rotas = 38 JSON + 2 multipart + 16 sem corpo; 13 de 13 decoys), aviso não clínico 3 + 1 + 0, órfãos 0, 0, 0; {TA-85} segue em 3 de 4. H1 provado em produção ({TA-42}): o Médico vinculado registra e a prescrição aparece na tela do aluno com data, autor e o aviso; vigência na leitura medida contra a aritmética de datas (1 dia hoje vale até hoje; 7 dias a partir de 7 dias atrás vale até ontem e não é vigente; 7 dias a partir de 6 dias atrás vale até hoje; 2 semanas valem até hoje mais 13; 1 mês a partir de 31/01 vale até 27/02; uso contínuo sem data de fim); consentimento ausente, restrito a outra especialidade e revogado dão 403, com o par positivo em cada negação; desativar só pelo autor ou pelo Super Administrador, repetido dá 409; data futura recusada. 96 combinações de largura, tema e tela sem rolagem horizontal. Correções extras do card, dentro do mesmo merge: (1) as datas exibidas passam a ser as de Brasília, em um ponto único do web (web/src/formato/data.ts), em todas as telas (antes a data de registro e de desativação saía em UTC e, entre 21h e 24h de Brasília, mostrava o dia seguinte; achado P2 do qa); data civil pura nunca passa por Date; (2) o nascimento do perfil passa a ser data pura AAAA-MM-DD (o campo abria vazio e o painel do aluno mostrava o instante cru; bug anterior ao card, achado pelo qa no reteste, P2-04); (3) o erro do seletor de data não cita mais o formato e a data da prescrição abre em hoje. Perdas ratificadas: nos formulários de anamnese, avaliação e exame o alerta de campos passa a sumir ao corrigir o último campo, e a recusa de anexo grande ganha os dois números (20 MiB mais um byte diz 20.971.521 B e 20.971.520 B). Lacunas declaradas: o qa não reavaliou o P3-03 (título e conteúdo do cartão de detalhe em eixos 21px afastados); depois das 21h de Brasília o servidor aceita data de até 2 dias à frente, a folga de fuso desenhada no Drift 6. {ART-FORM-08} NÃO se marca: {F5-06} o fecha. {F5-06}, {F5-10} e {F6-05} seguem abertos.
RF-SAUD-006 e RF-PAIN-005.
O que implementar: os campos tratamento realizado, exercícios realizados, notas de amplitude de movimento, notas de progresso e plano da próxima sessão, além da região, das duas medidas de dor, da duração e da data.
Aceite (H1): Dado um fisioterapeuta com vínculo ativo, Quando registra uma sessão com dor antes sete, depois três, e os cinco campos de texto preenchidos, Então a sessão aparece no histórico com a variação de menos quatro pontos e os cinco campos legíveis no detalhe, E o plano da próxima sessão aparece no painel do Fisioterapeuta, E um valor de dor fora da faixa é recusado, E a sessão sem os opcionais salva exibindo só o preenchido.
Nota (F1-09): entidade sensível de RN-001. A leitura desta entidade é servida pelo leitor único de registro sensível (server/src/acesso/leituraSensivel.ts), nunca por consulta própria da rota; rodar ops/verifica-leitura-sensivel.mjs antes do gate do qa, conforme a seção do gabarito em GABARITO-ACESSO-ALUNO.md.
Nota (F1-10): entidade removível de RF-CORE-008. Ao criar a tabela, aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md): coluna desativado_em/desativado_por (R-2 do gate {F1-10}), a política FOR DELETE exigindo o papel administrador e o gatilho de remoção, e rodar o verificador de verificarPoliticas.ts antes do gate do qa.
Nota (F1-12): entidade sensível de consentimento (RN-001). Ao criar a tabela, aplicar a quinta seção do gabarito (GABARITO-ACESSO-ALUNO.md, "Consentimento por categoria"): marcar categoriaConsentimento: 'sessao_reabilitacao' na entrada dela em ENTIDADES_E1; a política de leitura em conjunção com mfit.consentimento_visivel(aluno_id, 'sessao_reabilitacao'), além de mfit.pode_ler_aluno; o WITH CHECK de INSERT e de UPDATE em conjunção com mfit.tem_consentimento(aluno_id, 'sessao_reabilitacao'); o gatilho AFTER INSERT chamando mfit.auditar_criacao('aluno_id'); e rodar o verificador de {F1-12} antes do gate do qa, subindo o placar N de 6 no Plano de Testes (TA-10).
Nota [F1-18]: a tela nasce com <AvisoNaoClinico ponto="telas_clinicas" /> no topo, consumindo o texto vigente da tabela; não escreva o parágrafo no componente. O décimo terceiro verificador (ops/verifica-aviso-nao-clinico.mjs) reprova se esta tela existir sem ele, e o placar de RF-CORE-012 sobe de 2 para 3 e para 4 conforme elas nascerem.
Nota (Inspeção, gate {F5-01}, Drift 7, 2026-10-01): a Nota F1-18 acima passa a dizer: a aba desta tela nasce DENTRO da casca de /saude, criada em {F5-01}, que já traz <AvisoNaoClinico ponto="telas_clinicas" /> no topo e serve as quatro telas clínicas; não nasce rota de tela clínica fora da casca, e o verificador reprova aba clínica fora dela. O placar de RF-CORE-012 sobe de 2 para 3 com {F5-01}, e o quarto ponto é o pacote de exportação ({F8-09}); as abas de {F5-04} a {F5-06} não sobem o placar, porque o ponto 3 é uma tela só.
Nota (Inspeção, gate {F5-06}, Drifts 1 a 9 e migração 0062, 2026-10-06): publicados antes do código; nenhuma marcação muda e o H1 não muda. (1) Quem grava (Drift 1): o Fisioterapeuta com vínculo ativo e o Aluno para si, como dizem RF-SAUD-006 e a matriz (o pedido dizia só o Fisioterapeuta; vale o DRS), no molde do exame: aluno para si com especialidade_autor nula, ou Fisioterapeuta com o vínculo fisioterapeuta literal; Personal Trainer, Médico e Nutricionista são recusados. (2) Região (Drift 2, decisão do Fiscal): lista editável nova regiao_corporal (dezoito itens, com o lado onde há lado, mais "Outra"), porque o texto livre parte a série de dor de {F7-10}; as listas fechadas com consumidor passam de 9 para 10. É lista, e não constante, porque a região é agrupamento da série e não regra de cálculo (acrescentar ou renomear item não quebra nada; a unidade da duração da prescrição é C porque entra no cálculo). (3) Fronteira de {F7-10} (Drift 3): este card entrega SÓ o bloco "Próxima sessão" no painel do Fisioterapeuta (o plano da ÚLTIMA sessão ativa de cada paciente, só quando preenchido; plano antigo nunca sobe); carteira, evolução da dor por região e sem sessão recente ficam em {F7-10} (Nota lá). (4) Não se edita (Drift 4): só desativa, o autor ou o Super Administrador; Nota em RN-009 do DRS. A tabela nasce SEM explicacao_gerada (dono {F5-10}) e a notificação de criação (tabela M) fica em {F6-05}. (5) Campos (Drift 5): o H1 deste card diz "cinco campos" e o DRS diz "seis": são tratamento realizado, exercícios realizados, notas de amplitude, notas de progresso, plano da próxima sessão e observações; o H1 do card lê-se como os cinco do cenário, e o detalhe omite o opcional vazio, sem "Não informado" (Nota em {TA-43}). (6) Escalas (Drift 6): dor de 0 a 10 é constante estrutural (C, entra na variação do verificador, ESCALA_DE_DOR, com CHECK espelhado e decoy); duração de 1 a 300 minutos é constante técnica (C, precedente FAIXAS_AVALIACAO, CHECK espelhado); data não futura com a folga de fuso espelhada (o literal na política é o mesmo AMPLITUDE_DE_FUSO_EM_DIAS, com decoy) e criado_em = now() exigido na política. (7) Teto (Drift 7): um só parâmetro, reabilitacao_texto_longo_max_caracteres, 4000, faixa de 500 a 10000, para os seis campos longos; mfit.parametro passa de 42 para 43 linhas e o catálogo de 47 para 48 fichas (L.2, {ART-CFG-01}). (8) Aviso não clínico (Drift 8): a aba nasce dentro da casca de /saude; {TA-85} fica em 3 de 4 e este card não é dono do quarto ponto ({F8-09}). (9) Placares parciais, sem marcar (Drift 9): {TA-10} de 5 de 7 para 6 de 7; {TA-07} de 7 de 10 para 8 de 10; capacidade por especialidade do verificador de acesso de 20 para 22 entradas; rotas de corpo de 56 (38 JSON, 2 multipart e 16 sem corpo) para 58 (39, 2 e 17); {TA-85} não se move; o teste D-6.3 passa a usar foto_progresso como tabela inexistente, porque sessao_reabilitacao passa a existir. Tudo a medir no fechamento, e não a projetar. Migração deste card: 0062, a única (a categoria e o ponto sessao_reabilitacao já existem, migrações 0012 e 0050; nenhum ADD VALUE; retrato anterior: migração 0061, parâmetros 42, listas 9): cria a lista regiao_corporal e a tabela mfit.sessao_reabilitacao com as colunas do Apêndice (especialidade_autor nula ou fisioterapeuta, desativado_em e desativado_por, sem explicacao_gerada); leitura por pode_ler_aluno(aluno_id, autor_id, false, 'sessao_reabilitacao') em conjunção com consentimento_visivel; inserção no molde da 0059; atualização concedida somente em desativado_em e desativado_por; exclusão só do Super Administrador; gatilhos de auditoria; o parâmetro; nenhuma função nova; rollback escrito e ensaiado. Contrato: leitor único reabilitacao/reabilitacao.ts (a entrada de ENTIDADES_E1 troca o leitor); a variação (depois menos antes) é função pura com teste, calculada na leitura; rotas GET /api/reabilitacao (lista), GET /api/reabilitacao/:id, POST /api/reabilitacao, POST /api/reabilitacao/:id/desativar e GET /api/reabilitacao/proximas (só o Fisioterapeuta, pelo mesmo leitor). Ordem das recusas na criação: 401, Administrador 403, corpo 400, aluno, consentimento, vínculo, campos, data e só então o INSERT; byte nulo recusado. Perda declarada: o painel do Fisioterapeuta ganha o bloco "Próxima sessão"; os painéis de Personal, Médico e Nutricionista não mudam; o teste D-6.3 muda de alvo; nada mais. Contagens a medir no fechamento, e não a projetar: tabelas do schema de 35 para 36, parâmetros de 42 para 43, listas fechadas com consumidor 10. O cartão continua [ ].
RF-SAUD-007.
Nota (Inspeção, reconciliação, 2026-09-30): entidade sensível de consentimento na categoria avaliação física (RF-CORE-010, RN-001, E.1): aplicar a quinta seção do gabarito com categoriaConsentimento: 'avaliacao_fisica', mfit.tem_consentimento(aluno_id, 'avaliacao_fisica') no WITH CHECK de escrita e mfit.consentimento_visivel(aluno_id, 'avaliacao_fisica') na leitura; o aluno sem esse consentimento que tenta registrar a primeira medida recebe a tela de consentimento, sem linha gravada.
Aceite (H1): Dado um aluno com medição anterior de oitenta quilogramas, Quando registra setenta e oito, Então a tela exibe a variação de menos dois em relação à anterior, E o gráfico passa a conter dois pontos na série de peso, ordenados por data.
Nota (F1-10): entidade removível de RF-CORE-008. Ao criar a tabela, aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md): coluna desativado_em/desativado_por (R-2 do gate {F1-10}), a política FOR DELETE exigindo o papel administrador e o gatilho de remoção, e rodar o verificador de verificarPoliticas.ts antes do gate do qa.
Nota (Inspeção, gate {F4-05}, Drift 3, 2026-09-30): o bloco de peso e composição corporal do acompanhamento nutricional (RF-DIET-005) nasce aqui, lendo o leitor da medida corporal; {F4-05} não o entrega, porque a medida corporal só existe a partir deste card.
Nota (Inspeção, gate {F5-02}, Drift 9, 2026-10-01): a avaliação física não escreve medida corporal: {F5-02} não cria nem grava medida_corporal, embora as duas sejam da mesma categoria de consentimento (avaliação física). O consumidor "avaliação física" de M.3 (a referência corrente da última medida) é deste card.
RF-SAUD-008.
Nota (gate {AUD-07}, 2026-09-29): a lista nasce como linhas de mfit.lista_fechada_item (valor no CHECK de lista), nunca enum nem constante ({AUD-07}).
Nota (R-14, {F2-02}): o middleware de cabeçalhos, a guarda de tamanho, a detecção de formato pelos bytes e a ordem "objeto antes da linha" nascem em {F2-02}. Este card não constrói uma segunda via de arquivo: escreve no repositório privado reusando aquele desenho, e acrescenta o que é dele (consentimento, endereço assinado na leitura, auditoria de leitura sensível).
O que implementar: categoria de pose obrigatória (frente, lado direito, lado esquerdo, costas, lista editável), observação opcional, galeria agrupada por categoria e comparação restrita à mesma categoria.
Aceite (H1): Dado um aluno que consentiu a categoria foto de progresso, Quando envia duas fotos de frente em datas diferentes e uma de costas, Então a galeria agrupa as três pela categoria, E a comparação de frente oferece apenas as duas de frente, E as escolhidas aparecem lado a lado com as datas, E a categoria com uma única foto exibe comparação indisponível sem erro, E o endereço direto do arquivo, sem assinatura, retorna acesso negado.
Nota (F1-09): entidade sensível de RN-001. A leitura desta entidade é servida pelo leitor único de registro sensível (server/src/acesso/leituraSensivel.ts), nunca por consulta própria da rota; rodar ops/verifica-leitura-sensivel.mjs antes do gate do qa, conforme a seção do gabarito em GABARITO-ACESSO-ALUNO.md.
Nota (F1-10): entidade removível de RF-CORE-008. Ao criar a tabela, aplicar a seção de remoção do gabarito (GABARITO-ACESSO-ALUNO.md): coluna desativado_em/desativado_por (R-2 do gate {F1-10}), a política FOR DELETE exigindo o papel administrador e o gatilho de remoção, e rodar o verificador de verificarPoliticas.ts antes do gate do qa.
Nota (F1-12): entidade sensível de consentimento (RN-001). Ao criar a tabela, aplicar a quinta seção do gabarito (GABARITO-ACESSO-ALUNO.md, "Consentimento por categoria"): marcar categoriaConsentimento: 'foto_progresso' na entrada dela em ENTIDADES_E1; a política de leitura em conjunção com mfit.consentimento_visivel(aluno_id, 'foto_progresso'), além de mfit.pode_ler_aluno; o WITH CHECK de INSERT e de UPDATE em conjunção com mfit.tem_consentimento(aluno_id, 'foto_progresso'); o gatilho AFTER INSERT chamando mfit.auditar_criacao('aluno_id'); e rodar o verificador de {F1-12} antes do gate do qa, subindo o placar N de 6 no Plano de Testes (TA-10).
Nota (F1-16): ao criar a tabela da dimensão de armazenamento de anexos, o ramo novo entra no CASE de mfit.consumo_do_aluno (não mais em dentro_do_limite_do_aluno, que desde {F1-16} só decide); sem o ramo a função continua levantando exceção, de propósito, e a política nova nasce quebrando, nunca liberada em silêncio; e a tela /plano passa a exibir aquela dimensão sozinha, sem uma linha de código de front, não escrever uma segunda contagem para a tela.
RF-SAUD-009 e D.2.
Aceite (H1): Dado um arquivo de mídia de exercício e um de exame, Quando o endereço direto de cada um é acessado sem sessão, Então a mídia responde conteúdo e o exame responde acesso negado, E o exame só responde por endereço assinado gerado para quem tem direito, expirando em até quinze minutos.
Nota (R-14, {F2-02}): o middleware de cabeçalhos, a guarda de tamanho, a detecção de formato pelos bytes e a ordem "objeto antes da linha" nascem em {F2-02}. Este card não constrói uma segunda via de arquivo: escreve no repositório privado reusando aquele desenho, e acrescenta o que é dele (consentimento, endereço assinado na leitura, auditoria de leitura sensível).
Nota (Inspeção, gate {F5-04}, Drift 1, 2026-10-06): fronteira com {F5-04}, publicada antes do código. {F5-04} só EMITE o endereço assinado no detalhe do exame, pelo ponto que já existe (emitirEnderecoArquivoSensivel), e não toca assinar.ts, privado.ts, a faixa de 1 a 60 minutos de url_assinada_validade nem o H1 mídia x exame. Isto é deste card: fixar a validade de até quinze minutos do H1 contra a faixa publicada, a prova de que o exame responde acesso negado sem sessão e a correção do comentário do código que diz "download no nginx", que é falso (o arquivo privado sai pela aplicação, por endereço assinado: {ART-INT-02}).
RF-SAUD-010, ADR-008 e M.2.
O que implementar: o acionamento nos quatro registros, exame, prescrição, anamnese e sessão de reabilitação, com o texto guardado em campo próprio de cada um.
Aceite (H1): Dado um exame, uma prescrição, uma anamnese e uma sessão de reabilitação preenchidos do mesmo aluno, Quando o usuário aciona a explicação em cada um dos quatro, Então os quatro exibem o texto formatado com o aviso de que não substitui a avaliação do profissional, E o conteúdo original de cada registro permanece intacto, E gerar de novo substitui apenas a explicação daquele registro.
Nota (Inspeção, gate {F5-05}, Drift 2, 2026-10-06): a tabela mfit.prescricao nasce em {F5-05} COM observacoes e SEM a coluna de explicação gerada. Essa coluna é deste card, que a acrescenta à prescrição, por migração própria, ao entregar a explicação nos quatro registros. O H1 não muda e o cartão continua [ ]. Nota (Inspeção, gate {F5-06}, Drift 4, 2026-10-06): a tabela mfit.sessao_reabilitacao nasce em {F5-06} SEM as colunas de explicação gerada (explicacao_gerada e explicacao_gerada_em), que constam da ficha antiga de {ART-ENT-06}. Essas colunas são deste card, que as acrescenta à sessão de reabilitação, por migração própria, ao entregar a explicação nos quatro registros. O H1 não muda e o cartão continua [ ].
Definição de Pronto da Fase 5: nenhuma categoria sensível grava sem consentimento, todo arquivo sensível só sai por endereço assinado e a anamnese do aluno é uma só.
Fase 6, Comunicação
RF-COMU-001.
Aceite (H1): Dado um vínculo ativo, Quando o aluno envia uma mensagem, Então ela aparece na conversa dos dois com autor e horário, E um profissional sem vínculo com esse aluno não consegue abrir nem listar essa conversa.
Nota (R-4, {F1-19}): toda exibição de outro usuário (nome e foto do remetente) consome GET /api/perfil/:usuarioId (ou a função perfilPublicoReduzido, quando a leitura for no servidor); proibido SELECT próprio em mfit.usuario, mfit.perfil_aluno ou mfit.perfil_profissional. O quarto placar de ops/verifica-acesso-aluno.mjs reprova o arquivo novo que ler a tabela na mão, e a política por linha já nega telefone, nascimento, sexo, altura, identificador público, plano, status de acesso e e-mail.
RF-COMU-002.
Aceite (H1): Dado dois profissionais de especialidades diferentes com vínculo ativo no mesmo aluno, Quando um abre a conversa profissional daquele aluno e envia mensagem, Então o outro a recebe, E o aluno não enxerga essa conversa em nenhuma tela, E um terceiro profissional sem vínculo não consegue abri-la.
Nota (R-4, {F1-19}): toda exibição de outro usuário consome GET /api/perfil/:usuarioId (ou a função perfilPublicoReduzido, quando a leitura for no servidor); proibido SELECT próprio em mfit.usuario, mfit.perfil_aluno ou mfit.perfil_profissional. O quarto placar de ops/verifica-acesso-aluno.mjs reprova o arquivo novo que ler a tabela na mão, e a política por linha já nega telefone, nascimento, sexo, altura, identificador público, plano, status de acesso e e-mail.
RF-COMU-003.
Aceite (H1): Dado um grupo com o aluno e três profissionais, Quando um participante menciona um dos profissionais, Então todos recebem a mensagem e apenas o mencionado recebe a notificação de menção, E ao encerrar um vínculo o profissional deixa de receber mensagens novas mantendo o histórico do período em que participou.
Nota (R-4, {F1-19}): toda exibição de outro usuário (participantes, menção) consome GET /api/perfil/:usuarioId (ou a função perfilPublicoReduzido, quando a leitura for no servidor); proibido SELECT próprio em mfit.usuario, mfit.perfil_aluno ou mfit.perfil_profissional. O quarto placar de ops/verifica-acesso-aluno.mjs reprova o arquivo novo que ler a tabela na mão, e a política por linha já nega telefone, nascimento, sexo, altura, identificador público, plano, status de acesso e e-mail.
RF-COMU-004.
Aceite (H1): Dado qualquer conversa, Quando o usuário arrasta ou seleciona um arquivo para envio, Então o envio é bloqueado com a mensagem indicando a tela própria, E uma mensagem com endereço de internet é aceita e exibida como link clicável.
RF-COMU-005.
Aceite (H1): Dado um usuário com doze notificações não lidas, Quando aciona limpar todas, Então a contagem vai a zero e a lista fica vazia sem recarregar, E cinco mensagens seguidas da mesma conversa aparecem como uma notificação agrupada com o contador cinco.
Nota (Inspeção, gate {F5-04}, Drift 5, 2026-10-06): a notificação aos Médicos com vínculo ativo, quando um exame é criado (Ação no envio do Apêndice de formulários, {ART-WF-07}), é entregue NESTE card e não em {F5-04}, no precedente do Drift 6 de {F4-03}. {F5-04} grava o exame e o mostra nas listas sem notificar; o H1 de {F5-04} e o caso {TA-41} não pedem o aviso. Quando este card entregar, o aviso nasce uma por destinatário, respeitando a restrição de visibilidade da categoria exame ({ART-WF-07}).
Nota (Inspeção, gate {F5-05}, Drift 3, 2026-10-06): a notificação de prescrição criada (Ação no envio do Apêndice de formulários, {ART-WF-07}), a do aluno inclusive, é entregue NESTE card e não em {F5-05}, no precedente do Drift 5 de {F5-04}. {F5-05} grava a prescrição e a mostra nas listas sem notificar; o H1 de {F5-05} e o caso {TA-42} não pedem o aviso. Quando este card entregar, o aviso respeita a restrição de visibilidade da categoria prescrição, como na Nota de {F5-04} acima. O H1 não muda e o cartão continua [ ].
Nota (Inspeção, gate {F5-06}, Drift 4, 2026-10-06): a notificação de sessão de reabilitação criada (Ação no envio do Apêndice de formulários, {ART-WF-07}) é entregue NESTE card e não em {F5-06}, no precedente de {F5-04} e {F5-05}. {F5-06} grava a sessão e a mostra nas listas sem notificar; o H1 de {F5-06} e o caso {TA-43} não pedem o aviso. Quando este card entregar, o aviso respeita a restrição de visibilidade da categoria sessão de reabilitação, como nas Notas acima. O H1 não muda e o cartão continua [ ].
RF-COMU-006.
Aceite (H1): Dado dois usuários com a mesma conversa aberta, Quando um envia uma mensagem, Então ela aparece na tela do outro em até três segundos sem recarregar, E o contador do sino é incrementado na mesma janela de tempo.
Definição de Pronto da Fase 6: as três formas de conversa funcionam com o isolamento correto, nenhum arquivo entra pelo chat e a notificação não acumula repetição.
Fase 7, Engajamento, painéis e relatórios
RF-ENGA-001.
Aceite (H1): Dado um aluno recém-cadastrado, Quando abre o painel, Então vê os quatro passos, nenhum marcado, E ao registrar a primeira refeição o passo correspondente aparece marcado sem ação manual, E cumpridos os quatro o bloco deixa de ser exibido.
RF-ENGA-002 e RN-005.
Aceite (H1): Dado um aluno com sessões em três dias consecutivos, Quando abre o painel no quarto dia sem ter treinado, Então a sequência exibida é três, E se o quinto dia passar sem sessão a sequência passa a zero.
RF-ENGA-003.
Aceite (H1): Dado um aluno com sessões em quatro dias distintos nas últimas doze semanas, Quando abre o calendário, Então exatamente quatro casas aparecem preenchidas nas datas corretas, E dias fora da janela não aparecem no mapa.
RF-ENGA-004.
Aceite (H1): Dado um aluno com nove sessões e a conquista de dez ainda não concedida, Quando conclui a décima, Então a conquista consta como concedida com a data de hoje e gera notificação, E não é concedida uma segunda vez em sessões posteriores.
RF-ENGA-005.
Aceite (H1): Dado um aluno com cinco sessões contendo o mesmo exercício, Quando o escolhe no gráfico, Então a série tem cinco pontos ordenados por data com a carga máxima de cada sessão, E ao escolher um exercício nunca executado o gráfico exibe o estado vazio, sem erro.
RF-PAIN-001.
O que implementar: o bloco de treino em primeiro lugar e no maior destaque da tela, com o atalho de retomar quando houver sessão em andamento e, na ausência dela, o treino do dia com a ação de iniciar; os demais blocos abaixo dele.
Aceite (H1): Dado um aluno com ficha ativa, refeições e medidas, Quando abre o painel em trezentos e setenta e cinco pixels de largura, Então todos os blocos aparecem empilhados sem rolagem horizontal, E o bloco de treino é o primeiro da pilha, acima de nutrição, medidas, sequência, calendário, conquistas e completude, E cada bloco exibe o dado real do aluno ou o próprio estado vazio, nunca um valor de exemplo, E existindo sessão em andamento o atalho de retomar ocupa o topo do bloco de treino.
Nota (F1-16): o consumo dos limites já tem tela e rota (/plano, GET /api/meu-plano); se o painel exibir consumo, consome a mesma rota, nunca conta por conta própria.
RF-PAIN-002.
Aceite (H1): Dado um Personal Trainer com quatro alunos ativos e uma solicitação aberta, Quando abre o painel, Então vê os quatro alunos e o indicador de uma solicitação aberta, E nenhum aluno de outro profissional aparece na lista.
Nota (R-4, {F1-19}): a carteira de alunos (nome e foto de cada aluno vinculado) consome GET /api/perfil/:usuarioId (ou a função perfilPublicoReduzido, quando a leitura for no servidor); proibido SELECT próprio em mfit.usuario, mfit.perfil_aluno ou mfit.perfil_profissional. O quarto placar de ops/verifica-acesso-aluno.mjs reprova o arquivo novo que ler a tabela na mão, e a política por linha já nega telefone, nascimento, sexo, altura, identificador público, plano, status de acesso e e-mail.
Nota (Drift 3, {F2-08}): a carteira aqui e a lista do aluno em /perfil herdam o mesmo teto de exibição de GET /api/vinculos (parâmetro vinculo_convites_pendentes_teto_linhas, hoje 50, sem paginação); a evolução da lista para paginação real é deste card ({F7-07}).
RF-PAIN-003.
Aceite (H1): Dado um nutricionista com três pacientes, Quando abre o painel, Então vê os três com a aderência às metas dos últimos sete dias e o plano vigente de cada um, E o painel exibido é o da especialidade nutricional, não o de outra.
Nota (R-4, {F1-19}): a carteira de pacientes (nome e foto) consome GET /api/perfil/:usuarioId (ou a função perfilPublicoReduzido, quando a leitura for no servidor); proibido SELECT próprio em mfit.usuario, mfit.perfil_aluno ou mfit.perfil_profissional. O quarto placar de ops/verifica-acesso-aluno.mjs reprova o arquivo novo que ler a tabela na mão, e a política por linha já nega telefone, nascimento, sexo, altura, identificador público, plano, status de acesso e e-mail.
RF-PAIN-004.
Aceite (H1): Dado um médico com dois pacientes e um exame enviado ontem, Quando abre o painel, Então o exame aparece na lista de recentes com nome do paciente e data, E exames de pacientes sem vínculo ativo com ele não aparecem.
Nota (R-4, {F1-19}): a carteira de pacientes (nome e foto) consome GET /api/perfil/:usuarioId (ou a função perfilPublicoReduzido, quando a leitura for no servidor); proibido SELECT próprio em mfit.usuario, mfit.perfil_aluno ou mfit.perfil_profissional. O quarto placar de ops/verifica-acesso-aluno.mjs reprova o arquivo novo que ler a tabela na mão, e a política por linha já nega telefone, nascimento, sexo, altura, identificador público, plano, status de acesso e e-mail.
Nota (Inspeção, gate {F5-05}, Drift 7, 2026-10-06): RF-PAIN-004 promete "prescrições vigentes", e a vigência se calcula porque a duração da prescrição nasce estruturada em {F5-05}: quantidade inteira maior que zero mais unidade (dias, semanas ou meses), ou a marca "uso contínuo". A vigência termina na data da prescrição mais a duração menos 1 dia, e "uso contínuo" fica vigente até ser desativada. A unidade é constante estrutural com restrição no banco, não lista editável. Este card consome as colunas duracao_quantidade, duracao_unidade e uso_continuo pelo leitor único de registro sensível, nunca por consulta própria, e calcula a vigência na leitura, sem guardá-la. O H1 não muda (ele trata de exames) e o cartão continua [ ].
Vigência (Inspeção, 2026-10-06): o dia da prescrição é o primeiro dia; vigente_ate = data da prescrição + duração - 1 dia (para semanas e meses, a mesma regra sobre a data somada), e a prescrição é vigente quando a data de hoje é menor ou igual a vigente_ate; uso contínuo é vigente até ser desativada. "Hoje" é a data no fuso America/Sao_Paulo, calculada num ponto único reutilizado por {F5-05} e {F7-09}, nunca a data UTC do banco. Exemplo: 7 dias a partir de 01/10 vale até 07/10.
RF-PAIN-005.
Aceite (H1): Dado um fisioterapeuta com um paciente que teve três sessões no joelho esquerdo, a última com plano da próxima sessão preenchido, Quando abre o painel, Então vê a evolução da dor daquela região com os três pontos na ordem das datas, E vê o plano da próxima sessão sem abrir o detalhe, E o paciente cuja última sessão não trouxe plano aparece sem esse trecho, E um paciente sem sessão nos últimos trinta dias aparece marcado como sem sessão recente.
Nota (R-4, {F1-19}): a carteira de pacientes (nome e foto) consome GET /api/perfil/:usuarioId (ou a função perfilPublicoReduzido, quando a leitura for no servidor); proibido SELECT próprio em mfit.usuario, mfit.perfil_aluno ou mfit.perfil_profissional. O quarto placar de ops/verifica-acesso-aluno.mjs reprova o arquivo novo que ler a tabela na mão, e a política por linha já nega telefone, nascimento, sexo, altura, identificador público, plano, status de acesso e e-mail.
Nota (Inspeção, gate {F5-06}, Drifts 2 e 3, 2026-10-06): (1) Fronteira (Drift 3): {F5-06} entrega SÓ o bloco "Próxima sessão" no painel do Fisioterapeuta: o plano da ÚLTIMA sessão ativa de cada paciente, somente quando preenchido, e o plano de uma sessão anterior nunca sobe (GET /api/reabilitacao/proximas, só o Fisioterapeuta, pelo leitor único). A carteira de pacientes, a evolução da dor por região e os pacientes sem sessão recente são entregues NESTE card, que reaproveita o bloco já existente em vez de refazê-lo. (2) Região (Drift 2): a região da sessão é um item da lista fechada editável regiao_corporal (dezoito itens, com o lado onde há lado, mais "Outra"), e a série de dor por região deste card agrupa por esse item; acrescentar ou renomear item da lista é decisão de administração e não muda nenhum cálculo, porque a região é agrupamento e não regra. O H1 não muda e o cartão continua [ ].
RF-PAIN-006 e RN-006.
O que implementar: o cálculo sobre as quatro fontes de RN-006, sessão concluída, refeição, medida e atividade externa (sincronizada, importada ou manual).
Aceite (H1): Dado um profissional com um aluno cuja última atividade foi há oito dias e outro há dois dias, Quando abre o painel, Então apenas o primeiro aparece na lista de inativos, E ao registrar atividade nova esse aluno sai da lista na abertura seguinte, E um terceiro aluno cuja única atividade nos últimos oito dias foi uma atividade externa não aparece na lista.
RF-PAIN-007.
Aceite (H1): Dado um sistema com números conhecidos de usuários e vínculos, Quando o Administrador abre o painel, Então cada indicador é igual à contagem correspondente na base, E ao criar um usuário novo o total aumenta em um na abertura seguinte.
RF-PAIN-008.
Aceite (H1): Dado o Administrador na tela de relatórios com período de trinta dias, Quando exporta o relatório de atividade, Então o arquivo contém exatamente as mesmas linhas exibidas na tela com o mesmo período, E o total do rodapé é igual à soma da coluna correspondente.
RF-PAIN-009.
O que implementar: uma tela só, servida aos dois papéis de administração, com as ações repartidas entre eles conforme RF-PAIN-009. Do Administrador: criar conta de aluno e de profissional, buscar, editar cadastro e especialidade e marcar registro profissional como verificado. Privativas do Super Administrador, negadas ao Administrador na rota e na política de acesso por linha e não apenas escondidas na interface: promover ou rebaixar papel, inclusive criar conta de Administrador; aplicar e remover o bloqueio administrativo da conta (qualquer papel, com autor e momento, mensagem própria); e excluir conta em definitivo (RF-CORE-008). O status de acesso por plano (só aluno, RF-PLAN-002) é do Super Administrador e não se confunde com o bloqueio administrativo.
Aceite (H1): Dado o Super Administrador na tela de usuários, Quando cria um usuário com papel de profissional e especialidade médica, Então ele entra com a senha inicial e cai no painel clínico, E ao receber o bloqueio administrativo a entrada é negada com a mensagem de conta bloqueada pela administração, diferente da mensagem de conta inativa por plano, E o registro guarda quem bloqueou e quando, E removido o bloqueio ele volta a entrar sem senha nova, E um aluno com acesso inativo por plano e sem bloqueio continua entrando e alcançando perfil, plano e leitura do histórico, E um Administrador na mesma tela cria e edita cadastro normalmente e é negado nas três ações privativas, promover ou rebaixar papel, bloquear ou desbloquear conta e excluir em definitivo, pela rota direta e pela política de acesso por linha, fechando 3 de 3.
Nota: a busca de alunos de {F1-14} é o mínimo do painel de atribuição; este card amplia ou generaliza GET /api/admin/alunos para todos os papéis, e não cria uma segunda rota de busca. perfil_aluno.status_acesso ({F1-14}) e usuario.bloqueado_pela_administracao (deste card) são os dois controles que RF-PAIN-009 exige que não se confundam, e esta tela mostra os dois separados e com rótulos diferentes.
Nota de escopo (onda AUD, {AUD-04}, 2026-09-15): este card foi reconciliado com RF-PAIN-009 na republicação da escada de acesso. Até esta data ele entregava ao Administrador três ações que passaram a ser privativas do Super Administrador, promover ou rebaixar papel, bloquear e desbloquear conta e excluir em definitivo, e um executor que o lesse ao pé da letra construiria a violação sem nunca contrariar o próprio card. A negação das três já existe no banco desde {AUD-04}, pelo gatilho de governança; o que nasce aqui é a negação na rota e a repartição da tela, e com ela fecham duas das três capacidades que ficam pendentes no lado negativo de {TA-90}.
Nota de escopo (onda AUD, {AUD-05}, 2026-09-15): a rota /admin/usuarios não nasce aqui. Ela nasce MÍNIMA em {AUD-05}, com a busca reusando a rota de busca de alunos que já existe e UMA única ação, Personificar, privativa do Super Administrador e com confirmação nominal, porque sem ponto de entrada o H1 de RF-CORE-014, o item de início do cartão {AUD-05} e o caso {TA-91} ficam os três sem caminho de tela, e um mecanismo que só responde por rota direta não é o que o requisito pede. Todo o resto desta tela continua sendo deste card, sem exceção: criar conta de aluno e de profissional, buscar para os demais fins, editar cadastro e especialidade, marcar registro profissional como verificado, promover ou rebaixar papel, aplicar e remover o bloqueio administrativo e excluir em definitivo. Este card passa a AMPLIAR uma rota que já existe em vez de criá-la, e o placar de {ART-SCR-03} sobe uma vez só, em {AUD-05}, nunca duas.
RF-ENGA-006 e L.2.
O que implementar: conjunto nomeado de campos vindo do parâmetro de configuração, percentual calculado sobre ele, lista dos faltantes com atalho por campo e desaparecimento do bloco em cem por cento.
Como verificar (teste primeiro): com um único campo preenchido, conferir que o percentual é a razão exata entre preenchidos e o tamanho do conjunto; preencher um a um até o bloco sumir.
Aceite (H1): Dado o conjunto de cinco campos de completude com um aluno que preencheu apenas a altura, Quando abre o painel, Então o indicador exibe vinte por cento e lista os quatro faltantes nominalmente, E o atalho de cada faltante leva ao campo correspondente, E ao preencher a data de nascimento passa a quarenta por cento e o campo sai da lista, E com os cinco preenchidos exibe cem por cento e o bloco deixa de ser exibido.
RF-PAIN-010, RF-CORE-008 e RN-008.
O que implementar: caixa de seleção por linha e seleção de todas as linhas do filtro corrente em treinos, histórico, usuários e exercícios; barra de ação em lote com a contagem; verificação de permissão registro a registro; auditoria por registro afetado.
Como verificar (teste primeiro): aplicar a ação em uma seleção de doze e comparar a contagem informada, os registros efetivamente alterados e o número de entradas de auditoria; repetir com aluno e profissional tentando a exclusão em definitivo.
Aceite (H1): Dado o Administrador na lista de exercícios com filtro exibindo doze linhas, Quando seleciona todas e aplica a desativação, Então exatamente doze ficam inativos, a mensagem informa doze e existem doze entradas de auditoria, E a mesma seleção por aluno ou profissional não oferece a exclusão em definitivo e, forçada, é negada nos doze sem afetar registro, E com um registro fora do alcance do papel a operação recusa aquele e conclui os demais, informando afetados e recusados.
RF-CORE-013, ADR-014 e ADR-015.
O que implementar: na tela de perfil, pedido de troca com endereço novo e senha atual; pedido pendente com cancelamento; endereço de confirmação de uso único enviado ao novo (reaproveitando o token e a fila de RF-CORE-005); efetivação na confirmação com aviso ao endereço antigo; registro na auditoria; pedido pendente visível e cancelável pelo Administrador em RF-PAIN-009.
Como verificar (teste primeiro): pedir a troca de A para B e tentar entrar com B antes de confirmar; confirmar e tentar entrar com A e com B; abrir o endereço de confirmação uma segunda vez; pedir com senha errada e com B já usado.
Aceite (H1): Dado um usuário autenticado com e-mail A, Quando pede a troca para B com a senha correta, Então o pedido fica pendente e B recebe o endereço de confirmação, E o login segue funcionando com A e recusa B até a confirmação, E após confirmar o login aceita B e recusa A, o endereço A recebe o aviso e a auditoria guarda autor, A e B, E o endereço de confirmação reaberto ou vencido é recusado sem efeito, E o pedido com senha errada, com B já usado ou inválido é recusado sem enviar e-mail.
Definição de Pronto da Fase 7: cada papel tem o painel da especialidade dele com dado real, o engajamento do aluno reage às ações e os relatórios batem com a base.
Fase 8, Cobrança, geração assistida e integrações
RF-COBR-001 e ADR-005.
Aceite (H1): Dado um vínculo ativo sem acordo, Quando o profissional registra valor, recorrência mensal e dia de vencimento, Então o acordo consta no vínculo e o aluno o enxerga na tela de cobrança, E toda tentativa de criação ou edição do acordo pelo aluno é negada.
Nota (F1-16): /plano não exibe valor cobrado, vencimento nem fatura, isso é /cobranca, e o valor de referência do plano continua sem leitor que decida coisa alguma (ADR-005).
RF-COBR-002 e RN-007.
Como verificar (teste primeiro): teste unitário da regra de geração, incluindo mês sem o dia contratado e acordo avulso.
Aceite (H1): Dado um acordo mensal iniciado há três meses com vencimento no dia dez, Quando o profissional abre a cobrança daquele aluno, Então existem exatamente três competências com vencimento no dia dez de cada mês e valor igual ao do acordo, E nenhuma competência de mês futuro foi criada.
Nota (F1-16): /plano não exibe valor cobrado, vencimento nem fatura, isso é /cobranca, e o valor de referência do plano continua sem leitor que decida coisa alguma (ADR-005).
RF-COBR-003.
Aceite (H1): Dado uma competência em aberto e vencida, Quando o profissional a marca como paga com a data, Então o status passa a paga e o total em aberto do aluno diminui exatamente naquele valor, E a marcação fica na auditoria com autor e momento.
Nota (F1-16): /plano não exibe valor cobrado, vencimento nem fatura, isso é /cobranca, e o valor de referência do plano continua sem leitor que decida coisa alguma (ADR-005).
RF-COBR-004.
Aceite (H1): Dado um aluno com acordos ativos com dois profissionais, Quando abre a tela de cobrança, Então vê dois blocos com acordo e competências, E o total em aberto exibido é igual à soma dos valores das competências em aberto.
Nota (F1-16): /plano não exibe valor cobrado, vencimento nem fatura, isso é /cobranca, e o valor de referência do plano continua sem leitor que decida coisa alguma (ADR-005).
ADR-008 e F.2.
O que implementar: contrato próprio de entrada e saída, chave do provedor apenas no servidor, tempo limite, nova tentativa e registro de uso por aluno para o limite do plano.
Como verificar (teste primeiro): trocar a implementação do provedor por uma simulação não altera nenhuma tela nem nenhum teste de requisito.
Aceite (H1): Dado a camada implementada, Quando o provedor está indisponível, Então a tela exibe a mensagem de indisponibilidade e mantém a composição manual, E nenhuma chave de provedor é enviada ao navegador.
Nota (F1-16): ao criar a entidade de uso de geração assistida (ADR-016), o ramo novo entra no CASE de mfit.consumo_do_aluno (não mais em dentro_do_limite_do_aluno, que desde {F1-16} só decide); sem o ramo a função continua levantando exceção, de propósito, e a política nova nasce quebrando, nunca liberada em silêncio; e a tela /plano passa a exibir aquela dimensão sozinha, sem uma linha de código de front, não escrever uma segunda contagem para a tela.
RF-IAGE-001.
Aceite (H1): Dado um aluno com preferências indicando cinco equipamentos, Quando o usuário aciona a geração, Então a proposta usa somente exercícios do catálogo compatíveis com esses equipamentos e respeita a duração preferida, E todo exercício proposto existe no catálogo, sem nome inventado.
RF-IAGE-002.
Aceite (H1): Dado um aluno com meta de duas mil calorias e preferência de quatro refeições, Quando aciona a geração, Então a proposta traz quatro refeições e o somatório fica dentro de mais ou menos dez por cento da meta, E nenhum alimento das restrições do aluno aparece na proposta.
RF-IAGE-003.
Aceite (H1): Dado uma geração concluída, Quando o usuário abre a prévia, Então nenhum registro existe ainda no banco, E após remover um item, alterar outro e salvar, o registro criado contém exatamente o conteúdo editado, E ao descartar nenhum registro é criado.
RF-INTE-001, RF-INTE-002 e ADR-011.
Aceite (H1): Dado um arquivo de exportação com dez atividades, Quando o aluno o importa, Então dez registros de atividade externa são criados e aparecem no calendário marcados com a origem, E reimportar o mesmo arquivo mantém dez, E ao solicitar a exportação o aluno recebe um pacote em que cada categoria tem o mesmo número de registros da base, com o aviso de natureza não clínica incluído.
Nota [F1-18]: o arquivo de abertura do pacote de exportação nasce com <AvisoNaoClinico ponto="pacote_exportacao" /> servido do lado do servidor, com o número de versão no nome ou no conteúdo do arquivo. O décimo terceiro verificador (ops/verifica-aviso-nao-clinico.mjs) reprova se o pacote existir sem ele, e o placar de RF-CORE-012 sobe de 2 para 3 e para 4 conforme os pontos nascerem.
RF-INTE-003, ADR-011 e F.2.
O que implementar: tela de integrações, fluxo de autorização delegada por provedor, credencial cifrada guardada apenas no servidor, estados da conta (conectada, precisa reautorizar, desconectada) e lista de provedores vinda da configuração.
Como verificar (teste primeiro): conferir que a credencial não trafega para o navegador em nenhuma resposta; abandonar uma autorização no meio e conferir que nenhuma conta é criada; desconectar e conferir que a sincronização seguinte não acontece.
Aceite (H1): Dado um aluno sem conta conectada, Quando conclui a autorização num dos serviços habilitados, Então a conta aparece conectada com data e permissões, E a sincronização seguinte cria as atividades do período, E ao desconectar a credencial é descartada, nenhuma sincronização nova ocorre e as atividades já importadas permanecem no histórico, E uma autorização recusada não cria conta nenhuma.
RF-INTE-004, K.1 e ART-JOB-07.
O que implementar: rotina periódica por conta conectada, chave de deduplicação por origem e identificador externo, campos de passos, distância e frequência cardíaca, e transição para o estado que exige nova autorização quando o serviço revoga.
Como verificar (teste primeiro): rodar a sincronização duas vezes no mesmo período e contar os registros; revogar a autorização do lado do serviço e conferir o estado da conta e a notificação.
Aceite (H1): Dado uma conta conectada cujo serviço tem doze atividades no período, Quando a sincronização roda, Então são criados doze registros com a origem nomeando o serviço, E rodando de novo o total permanece doze, E uma atividade nova leva o total a treze na execução seguinte, E revogada a autorização a conta passa a exigir nova autorização, o aluno é notificado e os treze registros permanecem.
RF-INTE-005, RN-005 e RN-006.
Aceite (H1): Dado um aluno com uma sessão concluída no sistema e duas atividades externas no mesmo dia, Quando abre o calendário, Então o dia aparece com as três, cada uma identificada pela origem, E a sequência de dias considera apenas a sessão do sistema (RN-005), E o profissional vinculado enxerga as mesmas três, E um aluno cuja única atividade nos últimos oito dias foi uma atividade externa não aparece na lista de inativos, por ser a quarta fonte de RN-006.
RF-EXER-005 e M.2.
O que implementar: acionamento por exercício e em lote para os exercícios sem mídia, prévia por exercício com aprovar e rejeitar, e gravação no catálogo somente após aprovação.
Como verificar (teste primeiro): com sete exercícios sem mídia, acionar o lote e conferir que o catálogo não mudou enquanto as prévias existiam; aprovar duas e rejeitar cinco, e contar quantos exercícios passaram a ter mídia.
Aceite (H1): Dado um catálogo com sete exercícios sem mídia, Quando o Administrador aciona a geração em lote, Então são produzidas sete prévias, uma por exercício, E nenhum exercício teve a mídia alterada enquanto as prévias não foram aprovadas, E ao aprovar duas e rejeitar cinco exatamente dois passam a ter mídia e os cinco rejeitados voltam a constar na lista de exercícios sem mídia.
Definição de Pronto da Fase 8: a cobrança fecha aritmeticamente, a geração assistida nunca grava sem revisão humana e o titular consegue levar os dados dele embora.
Fase 9, Guia da plataforma
Dependência: os módulos que o guia descreve já construídos (fases 1 a 8) e a camada interna de geração assistida (F8-05). Escrever o guia de um módulo que ainda vai mudar produz conteúdo que nasce errado.
RF-AJUD-001 e Apêndice do catálogo do guia.
O que implementar: entrada no topo do menu dos seis papéis, lista dos módulos com guia publicado para o papel de quem lê, página do guia com os sete blocos, versão e data, e estado vazio nomeando o módulo quando não há guia para aquele papel.
Como verificar (teste primeiro): publicar um guia com conteúdo distinto para dois papéis e abrir o mesmo módulo com cada um; conferir a entrada de menu nos seis papéis e o estado vazio num módulo sem guia.
Aceite (H1): Dado um guia publicado para o módulo de treino com conteúdo distinto para Aluno e para Personal Trainer, Quando um aluno abre o guia daquele módulo, Então lê o conteúdo do papel Aluno com os sete blocos presentes, E o mesmo módulo aberto pelo Personal Trainer exibe o conteúdo do papel dele, E a entrada do guia aparece no topo do menu dos seis papéis, E um módulo sem guia para aquele papel exibe o estado vazio nomeando o módulo, sem erro e sem conteúdo de outro papel.
RF-AJUD-002.
O que implementar: matriz módulo por papel na administração, rascunho separado da versão publicada, publicação criando versão numerada com data e autor, histórico consultável e restauração de versão anterior como versão nova.
Como verificar (teste primeiro): publicar, editar em rascunho e conferir que o leitor continua na versão anterior; restaurar a versão 1 e conferir que a 2 continua no histórico; tentar escrever no guia com cada papel não administrador.
Aceite (H1): Dado um guia na versão 1 publicada, Quando o Administrador edita e publica, Então nasce a versão 2 com data e autor e é ela que os leitores enxergam, E a versão 1 continua consultável com o conteúdo original, E enquanto a edição é rascunho os leitores continuam na versão 1, E restaurar a versão 1 cria a versão 3 sem apagar a 2, E toda escrita no guia por papel diferente de Administrador é negada.
RF-AJUD-003, ADR-008 e M.2.
O que implementar: acionamento por par módulo e papel, contexto montado com a descrição do módulo, os requisitos dele e as capacidades do papel, prévia editável com os sete blocos e publicação apenas por pessoa.
Como verificar (teste primeiro): gerar e conferir no banco que nenhuma versão foi criada antes da publicação; descartar e conferir que o guia continua sem conteúdo.
Aceite (H1): Dado um módulo sem guia para o papel Aluno, Quando o Administrador aciona a geração, Então é apresentada uma prévia com os sete blocos preenchidos, E nenhuma versão existe no banco enquanto a prévia não é publicada, E após editar dois blocos e publicar, a versão criada contém exatamente o conteúdo revisado, E ao descartar nenhuma versão é criada.
Definição de Pronto da Fase 9: todo par módulo e papel alcançável tem guia publicado com os sete blocos, o leitor nunca vê rascunho e nenhum conteúdo gerado chegou ao leitor sem revisão humana.
Rotinas de operação a implantar junto da Fase 8
DRS K.1. Geração diária de competências, marcação de vencidas, cálculo de inatividade, conferência de conquistas, sincronização das contas de atividade conectadas, expurgo de arquivo órfão e anonimização por retenção.
Como verificar (teste primeiro): executar cada rotina duas vezes no mesmo dia e conferir que nenhuma produz registro duplicado.
Aceite (H1): Dado as sete rotinas ativas, Quando cada uma é executada duas vezes seguidas, Então o estado do sistema após a segunda execução é idêntico ao estado após a primeira, E cada execução deixa registro com início, fim e quantidade processada.
DRS K.2 e K.3.
Aceite (H1): Dado o monitoramento ativo, Quando uma rota é forçada a erro por quinze minutos, Então o alerta correspondente é emitido, E uma restauração de teste da cópia diária reconstrói a base em ambiente separado com o tempo de recuperação registrado.