SICOP — Corrección del scaffolding V1 contra Prompt Maestro v2.0
Fecha: 2026-08-25 · Proyecto: mvp-crm · Estado: aplicado, suite verde (52 ejemplos)
Decisiones aplicadas
-
Bug en el brief, no en el código: la implementación literal de
LeadAssignment.next_business_startdel prompt maestro NO avanzaba de día cuando el lead llegaba después del cierre en día laboral (sáb 21:00 → devolvía sáb 08:00, en el pasado). Se corrigió añadiendot += 1.day if t.hour > BUSINESS_HOURS.end. → Actualizar el prompt maestro v2.0 con esta versión corregida. -
Enum
Lead.statusrenumerado: se insertóreserved: 6(negotiating → reserved → won) y se agregóinvalid: 10(faltaba; la Regla 3 escalawrong_number → invalid). won/lost/recycled cambiaron de valor entero. Seguro porque no había datos en ningún ambiente (repo sin commits, PG nunca migrado en prod). -
Migración original editada, no apilada: repo sin commits ni deploy; había que tocarla de todos modos por
jsonb → json(SQLite dev/test). Se añadieron en la misma migración:sla_starts_at,contact_score,sla_breached,recycle_date,campaign_iden leads;channel,outcome,to_statusnullable en lead_events; índices únicos parcialescontacts.phoneyleads[campaign_id, contact_id]. -
Stack realineado: SQLite dev/test (PG no corría en la máquina local; el stack lo mandata),
pgal group :production,sqlite3dev/test,punditañadido.config.time_zone = "America/Mexico_City". Solid Queue conectado en development. -
Webhook de Facebook: payload simplificado firmado con HMAC (
X-Hub-Signature-256) + GET hub.challenge. El mapeo del payload real de leadgen queda PENDIENTE del acceso a Meta Business (pregunta abierta #6 del brief). ENV:SICOP_FACEBOOK_APP_SECRET,SICOP_FACEBOOK_VERIFY_TOKEN. -
Regla 1 implementada: SLA corre desde
sla_starts_aten horario lun–sáb 08:00–19:59 CDMX. Lead fuera de horario → score abre el día en 100; monitor job se agenda parasla_starts_at. Score y breach persisten en columnas (calculados porLeadSlaMonitorJob, no en la vista). -
Idempotencia:
EscalateSlaBreachServiceretorna early sisla_breachedya es true;CreateLeadServicededup por campaign_id+phone y rescataRecordNotUniquedevolviendo el lead existente (webhook reintenta seguro).
Aplazado conscientemente (siguientes vertical slices)
- Namespacing de controllers (
bdc/,seller/,management/) — estructural, no urgente. ContactLeadService+ Regla 3 completa (marcar contactado con outcomeanswered).AssignToSellerService(reloj atómico con lock de fila).- Reciclaje (
recycle_dateexiste; falta el job que reaparece el lead). AppointmentPolicy,UserPolicy,AssignmentQueuePolicy(se crean con sus controllers).- Tailwind (stack lo lista; el scaffold usa application.css — decidir antes de UI seria).
Comandos
bundle config set --local without production && bundle install
bin/rails db:migrate && bin/rails db:test:prepare
bundle exec rspec # 52 ejemplos, 0 fallos
bundle exec rubocop # sin ofensas