Adiciona caminho paralelo de PIX manual estático pra unidades sem
integração Inter (Padova, Express AL). Mudança 100% aditiva — todas as
outras unidades continuam no fluxo Inter inalterado (default
pix_mode=inter_dynamic aplicado pela migration).
Backend (sem SOUL/SKILL ainda — Frente 7 vem depois):
- Migration concurrent: pix_mode + 4 campos manual_pix_* em captain_units;
provider + manual_proof_payload + manual_review_reason em captain_pix_charges
- Captain::Unit: enum pix_mode (prefix), validação condicional manual_*
- Captain::PixCharge: status estendido (awaiting_proof, pending_review),
scope manual/inter, nota interna ramificada por modo
- GeneratePixTool MCP: branch manual_static (chave fixa, mensagem direta
sem QR/Inter), preserva fluxo Inter intacto
- 4 tools MCP novas: verificar_comprovante_pix (vision gpt-5.3-codex),
criar_nota_interna (genérica), confirmar_reserva_pix_manual (wrapper
do ConfirmationService), marcar_reserva_pendente
- ConfirmationService: source_label cobre 'manual_pix_proof'
Próximos passos manuais (não inclusos neste commit):
1. Rodar migration em prod (entrypoint não roda no boot)
2. Seed Padova/Express com pix_mode=manual_static + chaves Stone
3. Deploy nova imagem via docker service update
4. Editar SOUL/SKILL Padova/Express na VPS Hermes + kill+boot
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captain::Assistant agora aponta direto pra Captain::Unit. Antes a relação
ia via CaptainInbox, mas isso quebrou quando re-apontamos as inboxes pros
Hermes — assistants captain_interno (Juliana, Bianca, Lara, Nina,
Valentina) ficaram SEM CaptainInbox associada e o lookup
unit_for(assistant) retornava nil.
Resultado: get_assistant_pricing(3) (Lara) caía no fallback de scenario
text. Construtor reportava "veio cenário/prompt, não tabela estruturada".
Migration adiciona captain_unit_id (FK opcional). Backfill explícito:
- 1 Juliana → unit 3 (Qnn01)
- 2 Bianca → unit 2 (PrimeAL)
- 3 Lara → unit 2 (PrimeAL — mesmo brand)
- 4 Nina → unit 5 (Express)
- 6 Valentina → unit 4 (Dolce Amore)
- 9 Lara.H → unit 2 (via parent_assistant_id=3)
Tools get_assistant_pricing_tool e save_agent_spec_tool atualizados pra
usar assistant.captain_unit primeiro (nova relação direta), com fallback
pro CaptainInbox se nulo (pra retrocompatibilidade).
Validado live: tool retorna grid markdown com Stilo/Alexa/Hidromassagem
em Seg-Qua + Qui-Dom corretamente.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ROOT FIX (não paliativo) das 3 lacunas que travavam o Construtor:
1. get_assistant_pricing_tool: lia de Captain::Mcp::PricingTables::TABLES
(hash Ruby) que NÃO EXISTE MAIS desde a migração pra DB. Caía no
fallback de scenario raw. Refactor: lê de Captain::PricingCategory +
Captain::PricingAmount, formata grid markdown agrupado por day_bucket.
2. save_agent_spec_tool: Construtor salvava REFERÊNCIAS
(pricing_source.copied_from_assistant_id) mas hermes-provision script
espera DADOS EXPANDIDOS (categories[] com amounts, soul_md+skill_md).
Refactor: tool agora EXPANDE server-side — busca PricingCategory do
parent, monta categories array, gera SOUL.md (template + identity +
disclosure_policy) e SKILL.md (template + pricing + rules + identity).
Output já é spec consumível pelo script.
3. Captain::PricingAmount::PERIODS: adicionado '1h' (Prime tem 1h).
4. Seed pras 3 units faltando: Hotel Recanto (1) + PrimeAL (2) + Qnn01
(3). Agora os 6 units existentes têm pricing em DB.
Hot-patched ambos tools + USR1 no Puma. Construtor pronto pra criar
Bianca/Juliana etc end-to-end sem intervenção manual.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migra a tabela de preços do PricingTables.rb hardcoded pras tabelas
captain_pricing_categories + captain_pricing_amounts no DB. Mantém a
mesma API pública Captain::Mcp::PricingTables.calculate(...) — código
chama o banco via novos modelos Captain::PricingCategory e
Captain::PricingAmount.
Seed db/seed_pricing_tables.rb faz backfill idempotente pra Dolce Amore
(unit 4) e Express (unit 5) com a mesma estrutura que tava no Ruby.
Adiciona em captain_assistants:
- hermes_subscription_secret (gerado pelo script de provisionamento)
- hermes_port (alocado no range 8650-8699)
- parent_assistant_id (link informativo Hermes → captain_interno parent
pra sombrear FAQs/scenarios via header X-Captain-Assistant-Id)
Adiciona em captain_units: extra_person_fee + currency.
Primeiro milestone do roadmap arquitetural pro Construtor autônomo
(decisões em memory/project_construtor_autonomo_decisions.md).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Marca cada Captain::Assistant com engine ('captain_interno' | 'hermes')
e move o roteamento Hermes do env var pro banco — admin troca engine
re-apontando a inbox no painel, sem deploy. Mantém fallback pras env
vars antigas (CAPTAIN_HERMES_INBOX_IDS etc) durante a migração gradual,
pra não quebrar Valentina antes da re-associação.
Frontend: badge "Hermes" (âmbar) ou "Interno" (cinza) ao lado de cada
assistant no dropdown switcher e no card da listagem, com chaves i18n
em en + pt_BR.
Tabela de preço (pricing_tables.rb): adiciona unit Express (id=5) e
estende a estrutura pra aceitar preço por dia da semana
(mon_wed/thu_sun) — necessário pro Express, retrocompatível com Dolce
Amore (preço único).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug reportado por cliente real: "valor extra não é a partir da 3ª pessoa?
Porque aí é para casal, ou seja sempre vai ter duas pessoas".
Cliente está certo. A base do quarto pra casal JÁ INCLUI 2 pessoas; taxa
extra começa na 3ª pessoa, não na 2ª. A documentação anterior (modelo +
skill Hermes + scenario 21 em prod) tinha "a partir da 2ª pessoa", o que
fazia Valentina cobrar extra pra ambos do casal — erro.
Mudanças:
- Texto da regra: "a partir da 3ª pessoa" pra Apartamento/Suítes/Mini
Chalé 45, com nota explícita "base do quarto já inclui 2 pessoas".
- Exemplo de cálculo refeito: 4 pessoas Suíte Master pernoite sex/sáb =
180 + (2 × 45) = R$ 270 (era 180 + 3×45 = 315, errado).
Categorias maiores (Chalé 2 Suítes, Suíte Ouro, Chalé Master 4 Suítes)
mantidas como antes (4ª e 8ª pessoa) — Rodrigo precisa revisar essas
separadamente já que cada uma tem capacidade diferente.
Aplicado em 3 lugares:
- db/seed_prompts/_modelos/scenarios/jasmine_dolce_amore__daniela_reservas.md
- /root/.hermes/profiles/valentina/skills/dolce-amore-reservas/SKILL.md
- DB do Captain prod scenario 21 (via update_columns, +363 chars)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dolce Amore (Valentina, assistant 6):
- Novo assistant principal + 5 cenários (daniela_reservas, disponibilidade_suites,
maria_fotos, outras_unidades, reclamacoes_ouvidoria) com tabela de preços
motel-first (Apartamento, Master, Luxo, Temática, Mini Chalé 45, Chalé 2 Suítes,
Chalé Master 4 Suítes, Suíte Ouro), regras Inter Pix, e tom adequado a motelaria.
- Cross-reference da Dolce nas 4 outras unidades (Express, PrimeAL, PrimeVL, Qnn01)
no scenario outras_unidades, com aviso "use só se cliente perguntar por Natal".
Handoff silencioso PrimeAL + Dolce (Bianca + Valentina):
- Mensagem ÚNICA "Um momento." substitui as antigas frases robotizadas
("vou te encaminhar pra atendente local...").
- Nova regra "ROTEIE PRO CENÁRIO PRIMEIRO": orquestradora deve sempre tentar
rotear pra cenário (handoff_to_daniela_reservas, handoff_to_maria_fotos, etc)
antes de considerar handoff humano.
- Nova regra "NA DÚVIDA, TRANSFERE": após descartar todos os 6 cenários, se a
pergunta não cabe em nenhum deles, handoff silencioso pra humano.
- Proibição explícita de mencionar nomes de cenário (Daniela, Maria) pro cliente.
- maria_fotos: REGRA #0 — se foto pedida não está na galeria (numeração inexistente,
característica fora do mapa, área não-suíte), responde "Um momento." + handoff
em vez de oferecer alternativa.
- daniela_reservas: novo gatilho de handoff "pergunta sobre reserva fora do prompt".
Rollback documentado em docs/captain/rollbacks/2026-05-01_handoff_silencioso.md
com snapshot completo dos prompts ANTES da mudança em prompts_snapshot.json,
permitindo reversão via update_columns.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pequenos ajustes em Captain::Unit (app + enterprise), migration de seed
inicial dos prompts Jasmine/Daniela, schema regenerado, e atualização do
README de seed_prompts pra refletir o estado atual dos modelos.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hook after_commit on:create no Captain::Unit dispara
ProvisionUnitInSupabaseJob, que upserta a unit em reserva_hotel.unidades
via Supabase REST (UNIQUE on tenant_id+chatwoot_unit_id) e grava IDs no
Captain::Unit (supabase_unit_id, supabase_tenant_id, supabase_marca_id).
Sem isso, criar nova unidade no painel Pix não habilitava roleta — a row
no Supabase ficava ausente e OfferService caía em "tenant não resolvido".
Inclui rake captain:reprovision_unit_in_supabase[id] + provision_all
pra reconciliação manual e migration retroativa.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bug raiz observado em conversa real (Rayssa Lorranny / PrimeAL, 25/04 12:44):
- Jasmine alucinou "não tenho a tabela exata por horas aqui neste momento"
- Pediu "qual dia?" quando cliente disse só "Valores" (deveria mostrar tabela completa)
- Mencionou "tabela qui-dom/feriado" entre parênteses na resposta (entrega que é IA)
3 mudanças aplicadas nos 4 daniela_reservas (PrimeAL, PrimeVL, Qnn01, Express):
1. Nova seção "🤖➡️👤 SE COMPORTE COMO HUMANA" no topo, com lista de frases
proibidas (todas que entregam IA) e exemplos de substituição humana
2. Nova "🚨 REGRA DE OURO — VALORES CURTO" antes de B): cliente disse só
"valor"/"valores"/"preço" sem especificar → manda tabela completa direto,
nunca pergunta dia/suíte primeiro
3. 3 proibições novas em "🚫 Proibições": dizer que não tem tabela,
mencionar "tabela qui-dom/seg-qua" na resposta, responder pergunta com pergunta
Inclui também o questionario_nova_unidade.md já postado no Mattermost top-team.
Refs: análise da conversa Rayssa Lorranny / Operacoes 2026-04-25
- Express: adiciona Singles/Família/Singles Duplo, Master qui-dom 5h, diárias preço único
- Qnn01: renomeia Master→Luxo, remove Pole Dance e 12h, Hidromassagem preço único
- PrimeAL/PrimeVL: estrutura seg-qua + qui-dom, Pernoite Especial Prime, hora excedente
- Adiciona docs/precos/ com tabelas oficiais por marca pra consulta humana
- Implementa rake task captain:sync_prompts lendo de _modelos/
- Liga a task no chatwoot_prepare → sync automático em todo deploy
Refs ROD-14 (Captain Review 2026-04-25)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Gerados usando o modelo validado do PrimeAL como base, adaptando:
- Nome do hotel, suítes, links (WhatsApp/Maps), saudação por unidade
- Tabela de preços específica de cada unidade
- Lista de outras unidades (exclui a própria, inclui as outras 8)
- Observação de atendimento exclusivo por unidade
Particularidades por unidade:
- Qnn01: 4 suítes (Standard/Master/Pole Dance/Hidromassagem), tabela seg-qua + qui-dom, tem 12h
- PrimeVL: 3 suítes (Stilo/Alexa/Hidromassagem), tabela seg-qua + qui-dom-feriado, tem 1h e hora excedente
- Express: 2 suítes (Standard/Master), tabela seg-qua + qui-dom, redireciona pra Prime quando cliente pede hidro
reclamacoes_ouvidoria.md é idêntico nas 4 unidades (framework LAST é universal).
Testado em staging: aplicado nos 3 assistants respectivos, scenarios novos criados (outras_unidades + Reclamacoes_Ouvidoria), FAQs de blocos de prompt deletados, FAQs de preço duplicados removidos. Aguardando validação via WhatsApp real.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Renomeia _prod_snapshot → _producao_atual (refletindo melhor o papel: snapshot do que está rodando hoje em prod, só leitura)
- Renomeia _staging_current → _modelos (modelos aperfeiçoados que vão virar nova prod)
- Todos os arquivos em _modelos/ agora usam o prefixo jasmine_<slug>__ (ex: jasmine_primeal.md), seguindo a mesma convenção já usada em _producao_atual/
- Atualiza README com a nova convenção e checklist de validação por unidade
Isso prepara a estrutura pra adicionar modelos das outras 3 unidades (Qnn01, PrimeVL, Express).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolve duas camadas de problema identificadas em teste end-to-end:
1. Embeddings falhavam com HTTP 404 (/codex/v1/embeddings não existe).
Solução: Captain::Llm::EmbeddingService sempre usa OpenAI tradicional
via Llm::Config.with_api_key(legacy_settings). ProviderConfig expõe
legacy_openai_settings pra isso.
2. Servidor Codex ocasionalmente responde com response.failed +
code=server_error (instabilidade transitória). Client agora retenta
até 2x com backoff exponencial (0.5s, 1.5s) em erros retryable:
HTTP 5xx, server_error no response.failed, ou stream inacabado.
Outras correções nesta etapa:
- Scenario#agent_model: em modo Codex, ignora CAPTAIN_OPEN_AI_MODEL_SCENARIO
(que pode ter gpt-4o legado) e usa ProviderConfig.model.
- ExtractionService/ContradictionCheckerService/TranslateQueryService:
trocam constantes hardcoded gpt-4o-mini/gpt-4.1-nano por
ProviderConfig.light_model (respeitando o provider ativo).
- ProviderConfig.DEFAULT_CODEX_MODEL agora é gpt-5.2 (reconhecido pelo
RubyLLM; gpt-5.4 não está no catalog do gem).
Validado ponta-a-ponta: WhatsApp → Chatwoot → Jasmine → handoff Daniela
→ faq_lookup com embedding OK → resposta com preços corretos.
Docs em docs/captain-codex-oauth.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reorganized db/seed_prompts/ into three clear bins:
_prod_snapshot/ — 16 prompts pulled from iachat_production
(4 Jasmines + 12 scenarios). Read-only baseline.
_staging_current/ — 6 prompts active in iachat-v2 right now
(Jasmine + 5 scenarios, including
outras_unidades and Reclamacoes_Ouvidoria
which were created on this branch).
target/ — empty for now. Source of truth: the seed
migration only writes from here. Files we
review and approve land here, then deploy
pushes them to prod.
Updated the seed migration to walk target/ and to support both
generic scenarios (apply to every unit) and unit-scoped scenarios
(file prefixed with assistant slug, only that unit). Empty files
are skipped — useful for staged rollouts.
This guarantees no prompt ships to prod by accident: only what
ends up in target/ is applied.
The orchestrator prompt (Jasmine) and scenario instruction (Daniela)
live in the database. When we merge this branch to main and deploy to
production, the prod DB will keep its OLD prompts — the new ones would
only exist in staging. That defeats the point of merging.
Fix: commit the current staging prompts as .md files under
db/seed_prompts/ and add a data migration that syncs them into the DB
on deploy. Idempotent (no-ops when content already matches).
From now on, prompt changes follow the same workflow as code: edit the
.md file, migration resyncs on deploy. The DB row becomes a mirror of
the file, not the source of truth.
Lays the data + job foundation for tracking customer interactions,
recurrence, and Pix conversion on Contact. Design decisions negotiated
with Rodrigo (see docs to come):
Rules:
- Gap of 30h from last message defines separate interactions
- Qualified interaction = >=2 customer msgs + >=2 attendant msgs,
both with textual content (>= 2 letters)
- One-shot consultation = >=1+1 but below the qualified threshold
(tracked as secondary KPI)
- Excludes contacts labeled `equipe_interna`
- is_recurring = interactions_count >= 2
- pix_generated_count counts all PixCharges; reservations_paid_count
only counts those with status = paid
Surface area:
- Migration adds denormalized stats to contacts + indexes for fast filtering
- Captain::ContactStats::InteractionCalculatorService computes the stats
for a single contact (pure, no persistence)
- Captain::Retention::RecalculateContactStatsJob persists them for one
contact (idempotent)
- Captain::Retention::RecalculateAllContactStatsJob runs daily at 3am BRT,
enqueues per-contact jobs for everyone active in the last 120 days
- Event-driven refresh: CaptainListener#conversation_resolved enqueues
recalc; Captain::PixCharge after_create/after_update enqueues recalc
on status change
No UI yet — that's the next layer.
Consolida o trabalho desta branch de abril/2026 em um bloco pronto pra
testar em staging antes do merge pra main.
## Correções de memória semântica
- ExtractionService: Princípio Zero + Regra de Ouro (ação consumada vs intenção).
- Cenário Daniela_Reservas: Passo 0 de classificação (consulta/intenção/fora).
## Roleta da Sorte (end-to-end)
- Schema Supabase + 7 RPCs atômicas (server-side, idempotentes).
- Services: Offer, Redeem, WeeklyReport.
- Jobs: OfferRouletteJob (hook em ConfirmationService após Pix pago),
NotifyRevealed + Scheduler de fallback.
- Tool manual GenerateRoletaLinkTool + endpoint público /roleta/notify.
- Dashboard /captain/roleta com Resgate + Relatório + anomaly detection.
## Cenário Reclamacoes_Ouvidoria
- Triagem P1-P4, framework LAST, Three-level listening, Self-check.
- Sem compensação material, detecção de cliente frustrado eleva prioridade.
## Analytics
- Funil de conversão /captain/funnel: 5 etapas via regex, zero LLM.
- Detector de churn via ChurnOutreach* (cron dias úteis 10h-17h BRT).
## Trabalho pré-existente incluído
- Captain Executive Reports (ceo_digest, mattermost_delivery).
- get_reserva_preco_tool, Lifecycle ajustes, Reservations UI polimentos.
## Outros
- .gitignore: patterns pra credenciais.
- Migrations de scenarios idempotentes.
- i18n completa pt_BR+en pra roleta/funnel.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Cria modelo LeadClick para registrar cliques das landing pages
- Cria modelo LandingHost para mapear hostname → inbox_id
- Endpoint público POST /track/click para receber eventos de clique
- Leads::AttributionMatcherService para correlacionar clique com conversa
- Integração com IncomingMessageWuzapiService para atribuição automática
- API REST para gerenciar LandingHosts por inbox (index/create/destroy)
- UI: nova aba 'Landing Pages' nas configurações da caixa de entrada
- Dashboard API client dedicado (landingHosts.js)
- RuboCop: refatora shift_signature_name, TrackingController, AttributionMatcherService e WuzapiService
- Arquitetura corrigida: templates agora pertencem à inbox (WhatsApp),
não à unidade PIX (que é uma config financeira, não de mensagens)
- Migration: troca FK captain_unit_id -> inbox_id (up/down explícito)
- Model: belongs_to :inbox; scope for_inbox
- Controller: escopo via account.inboxes.find(inbox_id)
- Rotas: move de captain/units/:id → inboxes/:id/notification_templates
- Scanner job: joins(:conversation).where(conversations: {inbox_id:})
- UI: página /captain/notifications com seletor de inbox no topo
(chips clicáveis, templates carregam por watch no selectedInboxId)
- i18n PT/EN: novas keys INBOX_LABEL, SELECT_INBOX_HINT, EMPTY
- Adiciona check_in_at/duration_hours ao schema do tool CreateReservationIntent
para que a IA capture o horário EXATO de chegada informado pelo cliente
- Cria captain_notification_templates: label, content, timing_minutes,
timing_direction (before/after), active, position
- Implementa SendNotificationService com interpolação de variáveis
(guest_name, check_in_time, check_out_time, suite_name, unit_name)
- Implementa NotificationScannerJob (Sidekiq-cron a cada 5min) com
janela de tolerância de ±5min e idempotência via metadata JSONB
- API REST: /captain/units/:unit_id/notification_templates (CRUD)
- Store Vuex captainNotificationTemplates + API client
- UI: página de gestão de templates com editor inline e botão '+'
- Configura rota captain_settings_notifications
- i18n PT/EN para todas as strings novas
- Rubocop e ESLint: zero offenses
Implementa a página Relatórios IA com geração de análises semanais
por IA baseadas nas conversas de cada unidade/caixa de entrada.
Funcionalidades:
- Página /settings/captain/reports com dois tabs (Insights IA / Operacional)
- Botão "Gerar Análise" que enfileira job Sidekiq
- Filtro por unidade ou caixa de entrada
- Exibe insights com status (pendente/processando/concluído/falhou)
- Mostra top_topics, ai_failures e period_summary
- Estado vazio com CTA para gerar primeiro relatório
Backend:
- InsightsController com endpoints index/show/generate
- GenerateInsightsJob que processa conversas com LLM
- ConversationInsightService com chunking e merge inteligente
- Migração para adicionar inbox_id à tabela captain_conversation_insights
- Link sidebar "Relatórios IA" em /settings/captain/reports
Frontend:
- Vuex store captainReports com actions/mutations/getters
- API client CaptainReportsAPI (getInsights, generateInsight)
- i18n en e pt_BR para CAPTAIN_REPORTS.*
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Melhorias na ferramenta send_suite_images para resolver confusão entre
categoria e número de suíte:
1. **Descrições de parâmetros mais claras**
- suite_category: exemplos específicos (Hidromassagem, ALEXA, STILO)
- suite_number: apenas números (101, 102, 103) - remove exemplos confusos
2. **Instruções explícitas no system prompt**
- Seção [Galeria de Fotos] com regras claras
- Prioriza suite_category quando ambíguo
- Evita confirmações desnecessárias com cliente
3. **Mensagens de erro melhoradas**
- Sugere buscar por categoria quando busca por número falha
- Feedback mais útil para a IA
Resultado esperado:
- Cliente: "Me manda foto da suite Alexa"
- IA: busca por suite_category="Alexa" ✓ (sem pedir confirmação)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Upgrade rails to 7.2.2 so that we can proceed with the rails 8 upgrade
afterwards
# Changelog
- `.circleci/config.yml` — align CI DB setup with GitHub Actions
(`db:create` + `db:schema:load`) to avoid trigger-dependent prep steps.
- `.rubocop.yml` — add `rubocop-rspec_rails` and disable new cops that
don't match existing spec style.
- `AGENTS.md` — document that specs should run without `.env` (rename
temporarily when present).
- `Gemfile` — upgrade to Rails 7.2, switch Azure storage gem, pin
`commonmarker`, bump `sidekiq-cron`, add `rubocop-rspec_rails`, and
relax some gem pins.
- `Gemfile.lock` — dependency lockfile updates from the Rails 7.2 and
gem changes.
- `app/controllers/api/v1/accounts/integrations/linear_controller.rb` —
stringify params before passing to the Linear service to keep key types
stable.
- `app/controllers/super_admin/instance_statuses_controller.rb` — use
`MigrationContext` API for migration status in Rails 7.2.
- `app/models/installation_config.rb` — add commentary on YAML
serialization and future JSONB migration (no behavior change).
- `app/models/integrations/hook.rb` — ensure hook type is set on create
only and guard against missing app.
- `app/models/user.rb` — update enum syntax for Rails 7.2 deprecation,
serialize OTP backup codes with JSON, and use Ruby `alias`.
- `app/services/crm/leadsquared/setup_service.rb` — stringify hook
settings keys before merge to keep JSON shape consistent.
- `app/services/macros/execution_service.rb` — remove macro-specific
assignee activity workaround; rely on standard assignment handlers.
- `config/application.rb` — load Rails 7.2 defaults.
- `config/storage.yml` — update Azure Active Storage service name to
`AzureBlob`.
- `db/migrate/20230515051424_update_article_image_keys.rb` — use
credentials `secret_key_base` with fallback to legacy secrets.
- `docker/Dockerfile` — add `yaml-dev` and `pkgconf` packages for native
extensions (Ruby 3.4 / psych).
- `lib/seeders/reports/message_creator.rb` — add parentheses for clarity
in range calculation.
- `package.json` — pin Vite version and bump `vite-plugin-ruby`.
- `pnpm-lock.yaml` — lockfile changes from JS dependency updates.
- `spec/builders/v2/report_builder_spec.rb` — disable transactional
fixtures; truncate tables per example via Rails `truncate_tables` so
after_commit callbacks run with clean isolation; keep builder spec
metadata minimal.
- `spec/builders/v2/reports/label_summary_builder_spec.rb` — disable
transactional fixtures + truncate tables via Rails `truncate_tables`;
revert to real `resolved!`/`open!`/`resolved!` flow for multiple
resolution events; align date range to `Time.zone` to avoid offset gaps;
keep builder spec metadata minimal.
- `spec/controllers/api/v1/accounts/macros_controller_spec.rb` — assert
`assignee_id` instead of activity message to avoid transaction-timing
flakes.
- `spec/services/telegram/incoming_message_service_spec.rb` — reference
the contact tied to the created conversation instead of
`Contact.all.first` to avoid order-dependent failures when other specs
leave data behind.
-
`spec/mailers/administrator_notifications/shared/smtp_config_shared.rb`
— use `with_modified_env` instead of stubbing mailer internals.
- `spec/services/account/sign_up_email_validation_service_spec.rb` —
compare error `class.name` for parallel/reload-safe assertions.
* chore: update scheduled messages author association to nullable and adjust related specs
* chore: update sender handling for WhatsApp messages and add external sender name
* feat: Adds model for scheduling messages
* feat: Implement scheduled message handling and processing jobs
* feat: Add ScheduledMessagesController and associated specs for managing scheduled messages
* refactor: Simplify scheduled message job specs and improve metadata handling
* feat: Add ScheduledMessagePolicy for managing access to scheduled messages
* feat: Add routes for managing scheduled messages
* feat: Add scheduled message event handling and broadcasting
* feat: Add JSON views for scheduled messages creation, destruction, updating, and indexing
* feat: Update scheduled message status and dispatch update event after message creation
* feat: Ensure scheduled message updates trigger dispatch event
* feat: Add mutation types for managing scheduled messages
* feat: Add additionalAttributes prop to Message component and provider
* feat: Implement scheduled message handling in ActionCable and Vuex store
* feat: Add unit tests for scheduled messages actions and mutations
* feat: implement scheduled messages functionality
- Added support for scheduling messages in the conversation dashboard.
- Introduced new components: ScheduledMessageModal and ScheduledMessages for managing scheduled messages.
- Enhanced ReplyBottomPanel to include scheduling options.
- Updated Base.vue to handle scheduled message styling.
- Integrated Vuex store module for managing scheduled messages state.
- Added necessary translations for scheduled messages in English and Portuguese.
* feat: add pagination to scheduled messages index and update tests accordingly
* chore: update scheduled messages specs for future time validation and response status
* chore: enhance scheduled messages API with pagination and add skeleton loader component
* feat: add create_scheduled_message action to automation rule attributes
* feat: implement create_scheduled_message action and enhance attachment handling
* feat: add scheduled message functionality with UI components and localization
* test: enhance scheduledMessages mutations tests with meta handling and structure
* chore: update label to display file name upon successful upload in AutomationFileInput component
* feat: add initialAttachment prop to ScheduledMessageModal and update ReplyBox to pass attachment
* chore: prepend_mod_with to ScheduledMessagesController for better module handling
* fix: attachment visibility in ScheduledMessageItem component
* chore: enhance ScheduledMessage model with validations and reduce controller load
* refactor: simplify ScheduledMessagesAPI methods by removing unnecessary instance variable
* chore: update event emission for scheduled message creation in ReplyBox and ScheduledMessageModal
* refactor: update status configuration to use label keys
* chore: update date formatting in ScheduledMessageItem component
* refactor: collapse logic to checkOverflow and update related functionality
* chore: add author indication for current user in scheduled messages
* chore: enhance scheduled message metadata with author information and localization
* fix: send message shortcut
* chore: handle errors in scheduled message submission
* chore: update scheduled message modal to use combined date and time input
* chore: refactor scheduled messages handling to remove pagination and update related tests
* fix: ensure scheduled messages update status and dispatch on failure
* fix: update scheduled message due date logic and simplify sending checks
* refactor: rename build_message method for send_message
* fix: update scheduled message creation time and improve test reliability
* chore: ignore unnecessary check
* chore: add scheduled message metadata handling in message builder, add scheduled message factorie and update specs
* refactor: use scheduled message factorie creation in specs
* chore: streamline error handling in scheduled message job and remove dispatch logic
* fix: change scheduled_messages association to destroy dependent records
* refactor: remove unused attributes from scheduled message payload builder
* chore: update scheduled message retrieval to use conversation association
* chore: correct cron format for scheduled messages job
* chore: remove migration for author_type in scheduled_messages
* feat: enhance scheduled messages management with delete confirmation and error handling
* chore: set cron poll interval to 10 seconds for improved scheduling precision
* feat: include additional_attributes in message JSON response
* feat: enhance scheduled message validation and localization support
* chore: update scheduled message display
* Merge branch 'main' into Cayo-Oliveira/CU-86aenh268/Mensagens-agendadas
* feat: add scheduled message indicators and validation for message length
* fix: remove unnecessary condition from line-clamp class binding
* feat: update scheduled messages localization and enhance content validation
* feat: update scheduled messages order, enhance scheduledAt computation, and add message association
* fix: reorder condition for Facebook channel message length computation
* fix: change detection for attachments in scheduled messages
* fix: remove unnecessary colon from close-on-backdrop-click prop in ScheduledMessageModal
* chore: add error handling for scheduled message deletion and update localization for delete failure
* fix: enforce minimum delay of 1 minute for scheduled messages and update validation
* fix: remove unused private property and improve locale formatting for scheduled messages
* fix: adjust positioning of DropdownBody in ReplyBottomPanel and clean up schema foreign keys
* docs: add scheduled messages management APIs and payload definitions
---------
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
CSAT scores are helpful, but on their own they rarely tell the full
story. A drop in rating can come from delayed timelines, unclear
expectations, or simple misunderstandings, even when the issue itself
was handled correctly.
Review Notes for CSAT let admins/report manager roles add internal-only
context next to each CSAT response. This makes it easier to interpret
scores properly and focus on patterns and root causes, not just numbers.
<img width="2170" height="1680" alt="image"
src="https://github.com/user-attachments/assets/56df7fab-d0a7-4a94-95b9-e4c459ad33d5"
/>
### Why this matters
* Capture the real context behind individual CSAT ratings
* Clarify whether a low score points to a genuine service issue or a
process gap
* Spot recurring themes across conversations and teams
* Make CSAT reviews more useful for leadership reviews and
retrospectives
### How Review Notes work
**View CSAT responses**
Open the CSAT report to see overall metrics, rating distribution, and
individual responses.
**Add a Review Note**
For any CSAT entry, managers can add a Review Note directly below the
customer’s feedback.
**Document internal insights**
Use Review Notes to capture things like:
* Why a score was lower or higher than expected
* Patterns you are seeing across similar cases
* Observations around communication, timelines, or customer expectations
Review Notes are visible only to administrators and people with report
access only. We may expand visibility to agents in the future based on
feedback. However, customers never see them.
Each note clearly shows who added it and when, making it easy to review
context and changes over time.
## Description
Change the url type from string to text, to support more than 255
characters
Fixes # (issue)
https://app.chatwoot.com/app/accounts/1/conversations/65240
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
## Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules