@@ -36,7 +36,11 @@ import * as fs from "node:fs";
3636import * as path from "node:path" ;
3737import pg from "pg" ;
3838
39- import { createPgAdminClient , completeTurnForEnrollment , type TurnResult } from "@/lib/followup/turn-bridge" ;
39+ import {
40+ createPgAdminClient ,
41+ completeTurnForEnrollment ,
42+ type TurnResult ,
43+ } from "@/lib/followup/turn-bridge" ;
4044import { carregarEnvLocal } from "../scripts/lib/env-de-teste" ;
4145
4246const env = carregarEnvLocal ( ) ;
@@ -71,7 +75,10 @@ async function main(): Promise<void> {
7175 case "prepare-agent-fixtures" : {
7276 const creds = loadCreds ( ) ;
7377 const fixtures = creds . followup_agent_fixtures ;
74- if ( ! fixtures ) throw new Error ( "followup_agent_fixtures ausente em .e2e-creds.json — rode seed-e2e-followup-agent.ts antes" ) ;
78+ if ( ! fixtures )
79+ throw new Error (
80+ "followup_agent_fixtures ausente em .e2e-creds.json — rode seed-e2e-followup-agent.ts antes" ,
81+ ) ;
7582 await pool . query ( `update ai_provider_credentials set validated_at = now() where id = $1` , [
7683 fixtures . credential_id ,
7784 ] ) ;
@@ -110,7 +117,32 @@ async function main(): Promise<void> {
110117 ) ;
111118 const conversationId = convRows [ 0 ] ! . id ;
112119
113- out ( { contactId, contactName, conversationId, channelSessionId : fixtures . channel_session_id } ) ;
120+ // A MENSAGEM PRECISA EXISTIR, e carimbada — mesma correção feita em
121+ // `e2e-elegibilidade-helpers.ts`, e pelo mesmo motivo.
122+ //
123+ // Esta fixture nasceu antes da fronteira do atendimento e criava o
124+ // silêncio como um CAMPO (`last_inbound_at`), sem linha em `messages`.
125+ // A varredura passou a exigir PROCEDÊNCIA: ela lê a mensagem inbound
126+ // mais nova e o carimbo dela, porque é isso que separa "calado neste
127+ // atendimento" de "calado desde outro". Sem a mensagem, a conversa é
128+ // invisível para o gatilho e o contato nunca é enrolado.
129+ //
130+ // O carimbo não se escreve aqui: `fn_service_inbound` dispara no INSERT
131+ // e o grava. Escrevê-lo à mão provaria a forma da linha, não o caminho.
132+ await pool . query (
133+ `insert into messages
134+ (organization_id, conversation_id, channel_session_id, contact_id,
135+ type, direction, status, sent_via, body, sent_at)
136+ values ($1, $2, $3, $4, 'text', 'inbound', 'received', 'ai', 'Oi, tudo bem?', $5)` ,
137+ [ creds . org_id , conversationId , fixtures . channel_session_id , contactId , lastInboundAt ] ,
138+ ) ;
139+
140+ out ( {
141+ contactId,
142+ contactName,
143+ conversationId,
144+ channelSessionId : fixtures . channel_session_id ,
145+ } ) ;
114146 break ;
115147 }
116148
@@ -177,7 +209,9 @@ async function main(): Promise<void> {
177209 case "get-enrollment" : {
178210 const enrollmentId = args [ 0 ] ;
179211 if ( ! enrollmentId ) throw new Error ( "enrollmentId obrigatório" ) ;
180- const { rows } = await pool . query ( `select * from followup_enrollments where id = $1` , [ enrollmentId ] ) ;
212+ const { rows } = await pool . query ( `select * from followup_enrollments where id = $1` , [
213+ enrollmentId ,
214+ ] ) ;
181215 out ( rows [ 0 ] ?? null ) ;
182216 break ;
183217 }
@@ -187,7 +221,8 @@ async function main(): Promise<void> {
187221 // ---- (a API de sweep não devolve id nenhum — é um cron fire-and-forget).
188222 case "find-enrollment" : {
189223 const [ orgId , pointerId , contactId ] = args ;
190- if ( ! orgId || ! pointerId || ! contactId ) throw new Error ( "orgId, pointerId, contactId obrigatórios" ) ;
224+ if ( ! orgId || ! pointerId || ! contactId )
225+ throw new Error ( "orgId, pointerId, contactId obrigatórios" ) ;
191226 const { rows } = await pool . query (
192227 `select * from followup_enrollments
193228 where organization_id = $1 and pointer_id = $2 and contact_id = $3
@@ -245,7 +280,12 @@ async function main(): Promise<void> {
245280 [
246281 "message.received" ,
247282 messageId ,
248- JSON . stringify ( { conversation_id : conversationId , contact_id : contactId , channel_session_id : channelSessionId , body_preview : body . slice ( 0 , 280 ) } ) ,
283+ JSON . stringify ( {
284+ conversation_id : conversationId ,
285+ contact_id : contactId ,
286+ channel_session_id : channelSessionId ,
287+ body_preview : body . slice ( 0 , 280 ) ,
288+ } ) ,
249289 orgId ,
250290 ] ,
251291 ) ;
@@ -298,7 +338,10 @@ async function main(): Promise<void> {
298338 `delete from followup_enrollment_events where enrollment_id in (select id from followup_enrollments where pointer_id = $1)` ,
299339 [ pointerId ] ,
300340 ) ;
301- const { rowCount } = await pool . query ( `delete from followup_enrollments where pointer_id = $1` , [ pointerId ] ) ;
341+ const { rowCount } = await pool . query (
342+ `delete from followup_enrollments where pointer_id = $1` ,
343+ [ pointerId ] ,
344+ ) ;
302345 out ( { ok : true , deleted : rowCount } ) ;
303346 break ;
304347 }
@@ -334,6 +377,9 @@ async function main(): Promise<void> {
334377}
335378
336379main ( ) . catch ( ( err ) => {
337- console . error ( "❌ e2e-followup-journey-helpers falhou:" , err instanceof Error ? err . message : err ) ;
380+ console . error (
381+ "❌ e2e-followup-journey-helpers falhou:" ,
382+ err instanceof Error ? err . message : err ,
383+ ) ;
338384 process . exit ( 1 ) ;
339385} ) ;
0 commit comments