Agenda da Lara¶
Agenda Cadência nativa por default, com contrato único para consultar, criar, alterar e cancelar compromissos; Google Calendar e Cal.com são providers opcionais implementados e cobertos por testes. Easy!Appointments também está implementado com testes mockados, mas ainda aguarda E2E contra ambiente vivo.
Por que foi construído assim¶
A Lara precisa agendar sem obrigar o cliente a obter OAuth ou contratar outra agenda. Por isso o provider native, persistido no próprio Supabase Cadência, é o default zero-config. Integrações externas continuam disponíveis atrás do mesmo contrato quando o negócio já depende delas.
O serviço centraliza idempotência, auditoria e resultado incerto. Timeout, 429 ou 5xx numa mutação pode significar que o compromisso foi criado sem resposta; repetir automaticamente produziria duplicatas.
Stack¶
| Componente | Tecnologia |
|---|---|
| Contrato/orquestração | FastAPI, SchedulingPlugin, SchedulingService |
| Agenda default | Supabase appointments + lara_scheduling_config |
| Providers externos | Google Calendar v3, Cal.com v2; Easy!Appointments REST com E2E vivo pendente |
| CRM | Contatos Cadência vinculados ao appointment |
| Segurança | Credenciais JSON cifradas no backend |
| Onde roda | cadencia-lara na VPS Master; página pública no cadencia-app |
Como funciona¶
flowchart TD
classDef component fill:#D1FAE5,stroke:#10B981,color:#111
classDef flow fill:#DBEAFE,stroke:#3B82F6,color:#111
classDef decision fill:#EDE9FE,stroke:#8B5CF6,color:#111
classDef core fill:#FEE2E2,stroke:#EF4444,color:#111
classDef external fill:#FEF3C7,stroke:#F59E0B,color:#111
classDef warning fill:#FEF9C3,stroke:#EAB308,color:#111
subgraph SG_component["Componentes"]
ui[("Projetos/Cadencia/Docs/agenda-lara.md")]
skills["Skills"]
service["SchedulingService"]
crm["CRM Cadência"]
end
class ui,skills,service,crm component
subgraph SG_flow["Fluxo do processo"]
request["Contato pede horário ou consulta um agendamento"]
availability["Consulta slots no provider efetivo do tenant"]
identity["Coleta os dados configurados pelo tenant e garante contato CRM"]
mutate["Cria, consulta, altera ou cancela com chave idempotente"]
reply["Só confirma o compromisso depois do sucesso"]
end
class request,availability,identity,mutate,reply flow
subgraph SG_decision["Decisões"]
provider["Provider definido?"]
free["Slot ainda cobre todo o período?"]
operation["Leitura ou mutação?"]
result["Resultado ok, duplicate, error ou unknown?"]
end
class provider,free,operation,result decision
title["Agenda da Lara"]
class title core
providers["Providers"]
class providers external
subgraph SG_warning["Gotchas / Erros"]
handoff["Resultado unknown"]
manual["Sistema clínico externo"]
end
class handoff,manual warning
request --> availability
availability -->|"slot escolhido"| identity
identity -->|"dados completos"| mutate
mutate -->|"ok/duplicate"| reply
availability -->|"decide"| provider
provider -->|"ausente: native"| availability
provider -->|"externo configurado"| providers
availability -->|"decide"| free
free -->|"sim"| identity
free -->|"não: oferecer outro"| availability
mutate -->|"decide"| operation
operation -->|"leitura"| reply
operation -->|"mutação"| result
result -->|"ok/duplicate"| reply
result -->|"unknown"| handoff
result -->|"error de leitura: pode repetir"| availability
crm -->|"se houver dupla digitação"| manual Sem configuração explícita, native calcula disponibilidade a partir do horário comercial do tenant menos appointments ativos. Criar agenda o compromisso e garante/vincula o contato do CRM. A página pública /agendar/[slug] usa endpoints server-to-server; o lead não acessa a API administrativa.
As skills verificam slot e só confirmam depois de sucesso. O schema genérico aceita nome, início, duração, telefone, email, assunto, local e observação, mas não marca esses campos como obrigatórios; regras adicionais de coleta pertencem à configuração/prompt do tenant e não são enforcement da tool. A consulta de agendamento lê o estado persistido; o alias legado agendar fica oculto para não duplicar tools.
Confirmação e reagendamento por resposta¶
Para tenants com confirmação habilitada, o lembrete D-1 termina com as respostas textuais CONFIRMAR e REAGENDAR. CONFIRMAR é tratado deterministicamente antes de billing, LLM e tools: confirma o appointment de forma idempotente e move a oportunidade para o estágio confirmado sem regredir o funil. Se a leitura de configuração estiver indeterminada, o fluxo falha fechado com uma resposta neutra; só retorna ao atendimento normal quando a feature está comprovadamente ausente ou desativada.
REAGENDAR não cancela nem troca a consulta de imediato. A Lara acolhe o pedido, pergunta se pode ajudar, consulta horários reais e só altera o appointment depois que o contato escolhe uma opção. Se a pessoa pede para esperar, ocorre uma tentativa consultiva de recuperação; uma recusa explícita é respeitada. Ao reagendar na agenda nativa, a confirmação volta a pendente e lembretes da data anterior ficam superseded, preservando auditoria.
Decisões técnicas¶
nativeé default e zero-config; providers externos são escolha explícita.- Unique parcial protege chave idempotente e conflito de slot no Postgres.
- Leituras falham como
errorretryable; mutações ambíguas falham comounknownsem retry cego. - Datas sem timezone usam o fuso do tenant; default
America/Sao_Paulo. - Credenciais externas ficam cifradas e nunca retornam ao painel.
- Cadências consultam a mesma disponibilidade pelo adaptador administrativo da Lara.
Gotchas & armadilhas¶
unknownnão é falha definitiva — o compromisso pode existir; humano deve conferir.- Sistema externo não sincronizado — dupla digitação manual pode deixar o Cadência desatualizado e permitir overbooking.
- Easy!Appointments ainda não tem prova ao vivo — backend implementado e testes mockados, mas o E2E contra ambiente real está pendente; o formulário também não expõe esse provider.
- Cal.com varia headers/versões — slots e bookings não compartilham contrato HTTP idêntico.
- Horário comercial não basta — provider e appointments ainda determinam o slot final.
Como operar¶
- Use
nativepara a agenda Cadência sem credenciais; configure horário/fuso/duração quando necessário. - Se usar provider externo, selecione-o e grave credenciais somente pelo backend.
- Valide disponibilidade e consulta no Playground.
- Teste criação/cancelamento num tenant de teste, inclusive replay idempotente e slot ocupado.
- Em
unknown, consulte o provider antes de qualquer nova mutação. - Se existir sistema clínico externo, defina integração ou controle formal da dupla digitação.
- Mudança na Lara passa primeiro pelo ambiente Attemys; deploy de produção exige API e worker no mesmo commit.
- O E2E usa appointment sintético e número controlado da equipe e termina limpando appointment, reminder, oportunidade, timeline e conversa.
Aceite controlado — Clínica OP, 14/08/2026¶
CONFIRMAR confirmou a fixture, moveu Avaliação Agendada para Avaliação Confirmada, gravou exatamente um evento de confirmação e um de mudança de estágio e permaneceu idempotente no replay. REAGENDAR preservou a consulta pendente, abriu o contexto e ofereceu horários reais; o replay não duplicou timeline nem movimento. A escolha final e a gravação do novo horário não foram executadas nesse E2E e continuam como limite explícito da validação.
O follow-up de não comparecimento permanece separado: ele exige no-show canônico por appointment e nunca deve disparar para consulta futura, cancelada, comparecida ou ainda sem status.
Validação técnica: pytest -q tests/scheduling/test_native.py tests/test_dev1364_booking_endpoints.py tests/test_dev1459_provider_default.py tests/test_dev1581_vincular_contato.py tests/test_dev1588_consultar_agendamento.py.
FAQ¶
Qual agenda funciona sem configuração? A agenda Cadência (native), sem OAuth ou chave externa.
Quais providers externos estão implementados? Google Calendar e Cal.com estão implementados e cobertos por testes do repositório. Easy!Appointments também está implementado com mocks, mas o E2E contra ambiente vivo segue explicitamente pendente.
Posso repetir uma criação após timeout? Não automaticamente. Primeiro confirme no provider, pois a mutação pode ter sido aplicada.
Agendamento cria o contato? Sim. A skill garante o contato no CRM e vincula o appointment quando há sucesso.
A agenda da Lara sincroniza um sistema clínico externo? Somente se existir integração específica. Cópia manual não atualiza automaticamente a disponibilidade.