Files
MP-Manager/CLAUDE.md
T
2026-05-30 14:31:19 -06:00

11 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Este repo ya tiene un AGENTS.md con documentación operativa detallada (comandos, gotchas de GHL, paginación, reglas de negocio Monte Providencia). Léelo siempre primero. Este archivo cubre el panorama arquitectónico que no es obvio leyendo un único módulo.

Comandos esenciales

  • python -m pip install -r requirements.txt — instalar dependencias.
  • python main.py — levanta FastAPI/Uvicorn en http://127.0.0.1:8000 con reload.
  • start.bat / stop.bat / restart.bat (Windows) — lanzan/matan/reinician la app y liberan el puerto 8000. restart.bat detecta automáticamente si estaba en modo normal o perfil persistente (lee generated/runtime/last_mode), mata Chromium zombies y limpia batch files huérfanos en generated/runtime/batch/.
  • python -m py_compile main.py db.py ghl_client.py sync_engine.py script_runner.py paths.py — único "lint" disponible (no hay test/lint/format configurado).
  • Scripts de utilidad se corren directo: python scripts\<nombre>.py [args]. La mayoría leen generated/data/mp_manager.sqlite (vía paths.DB_PATH) y asumen una sync previa.
  • Variables de entorno:
    • SYNC_ENGINE_MAX_WORKERS (default 1000, sin tope) — paralelismo de "Sincronizar Todo". La concurrencia efectiva es min(total_accounts, env_var), así que el default sincroniza todas las cuentas a la vez. El rate limit de GHL es por location, así que paralelizar más cuentas no provoca 429; bajar este valor solo tiene sentido para acotar uso de CPU/red local.
    • SCRIPT_RUNNER_MAX_WORKERS (default 4, máx 20) — paralelismo del runner del dashboard. Subir esto aumenta riesgo de 429 porque los subprocesos no comparten rate limit.

Arquitectura — el panorama

Patrón general: el repo es un panel de control FastAPI sobre SQLite que cachea datos de varias cuentas (locations) de Go High Level y orquesta scripts Python que pueden auditar o mutar datos directamente en GHL.

Flujo de datos

CSV de tokens ──► sync_engine ──► ghl_client (API GHL) ──► db.py (SQLite)
                                                                │
                                       ┌────────────────────────┘
                                       ▼
                            main.py (FastAPI)  ◄── templates/index.html + static/js
                                       │
                                       ▼
                            script_runner ──► subprocess scripts/*.py ──► (audita en script_audit, muta en GHL)

Módulos núcleo (raíz del repo)

Módulo Rol
main.py Punto de entrada FastAPI. Define endpoints, middleware de request_id, manejadores de excepción que escriben a error_log. En startup carga TOKENS_CACHE desde el CSV — los tokens nunca tocan SQLite.
paths.py Fuente única de verdad para rutas en disco. Todo lo generado por la app/scripts (DB, reports, exports, logs, screenshots, sesiones Playwright, snapshots de migración, estado de runtime) vive bajo generated/. Cualquier módulo nuevo debe importar sus paths desde aquí — nunca hardcodear.
db.py Capa SQLite. Define el esquema (accounts, contacts, pipelines, opportunities, workflows, sync_log, error_log) y operaciones de lectura/escritura. Cada save_* por location se hace en una transacción que reemplaza los registros previos de esa location.
ghl_client.py Cliente HTTP de GHL. Implementa rate limiting (110ms entre peticiones por token), reintentos con backoff exponencial para 5xx y lineal para 429, sesión TCP persistente por hilo. Es la fuente de verdad del comportamiento GHL.
sync_engine.py Orquesta sync_account (pipelines → opportunities → contacts → guardado transaccional) y sync_all_accounts (ThreadPoolExecutor con SYNC_ENGINE_MAX_WORKERS). Lee el CSV en cada arranque y sincronización.
script_runner.py Lanza scripts como subprocesos, encola sus logs para SSE, maneja ejecución paralela vs secuencial, asocia cada run a un run_id para auditoría. Sólo expone los scripts listados en SCRIPTS_METADATA.
script_audit.py Sistema de auditoría/rollback paralelo: script_runs, script_change_log, script_run_control. Los scripts mutadores deben registrar cada cambio aquí (estado plannedapplied → opcionalmente rolled_back) para que el dashboard pueda revertirlos por run_id.
error_logging.py Centraliza logging técnico. Cada error retorna un error_id que se devuelve al cliente y se persiste en error_log.

Carpeta generated/

Todos los archivos que produce la app o los scripts viven bajo generated/. La raíz del repo solo contiene código fuente, docs, configs estáticos (CSVs de tokens y verificador) y script_metadata_overrides.json (config viva del dashboard).

generated/
├── data/           mp_manager.sqlite (+ -shm, -wal)
├── reports/        outputs de scripts de auditoría (audit_custom_fields/, duplicados/, drift/, coverage/)
├── exports/        descargas del dashboard (servido vía /api/exports/{filename})
├── logs/           errors.jsonl + script_runs/ para runs futuros
├── migrations/     snapshots pre-destructive de scripts mutadores
├── browser/        session.json (Playwright shared), profile/, screenshots/
├── runtime/        server_info.json, last_mode, batch/ (bulk batches en vuelo)
└── _archive/       basura legacy fechada

Está ignorada en .gitignore y .megaignore. Para añadir un nuevo destino, agregar la constante en paths.py y registrar el directorio en ensure_dirs().

Cuenta principal vs sucursales

El location_id GbKkBpCmKu2QmloKFHy3 es la cuenta de Marca principal (Monte Providencia). Está hardcodeado en sync_engine.parse_accounts_csv y scripts/common.py (BRAND_LOCATION_ID). Todas las demás filas del CSV son sucursales. La sincronización de contactos es bidireccional Marca↔Sucursal; la de oportunidades es unidireccional Sucursal→Marca (la sucursal tiene prioridad). Ver AGENTS.md para las reglas completas.

Custom fields dinámicos

Los IDs de custom fields varían por sucursal. Nunca los hardcodees. Resuélvelos por nombre via los endpoints GET /objects/?locationId=… y GET /objects/{contact|opportunity}?locationId=… (header Version: 2021-04-15). scripts/common.py expone SchemaResolver y FIELD_ALIASES para esto — úsalo en scripts nuevos en vez de reinventar.

Convenciones de scripts

Los scripts viven en scripts/ y se categorizan por nombre:

  • Read-only: audit_*, mp_*_search, analyze_*, find_*, check_*, health_check_*, *_readonly, daily_summary_*.
  • Mutadores (escriben en GHL): fix_*, migrate_*, move_*, update_*, sync_contact_*, cleanup_*, reconcile_*, fill_*. Estos deben soportar --dry-run y registrar cambios en script_audit con su --run-id.
  • Todos los scripts comparten utilidades en scripts/common.py (carga de cuentas, resolver de schemas, normalización de nombres).
  • Para que un script aparezca en el dashboard hay que agregarlo a SCRIPTS_METADATA en script_runner.py (título, descripción, categoría, opciones, mutator: True si aplica).

Frontend

templates/index.html + static/js/app.js + static/css/style.css son el SPA. Consume los endpoints /api/* y streamea logs de scripts via SSE en /api/scripts/stream/{task_id}.

Automatización con navegador

scripts/ghl_browser_*.py usan Playwright contra la UI web de GHL para cosas que la API no soporta (renombrar/eliminar workflows, p.ej.). Soportan dos modos de sesión: shared storage_state en generated/browser/session.json (default) o perfil Chrome persistente vía GHL_BROWSER_PROFILE_DIR (más estable, default generated/browser/profile, arrancar con start_persistent_profile.bat). El auto-login con 2FA por correo (IMAP) se configura en .env (ver .env.example).

Antes de tocar estos scripts:

  • docs/PLAYWRIGHT_SESSION.md — manejo de sesión, modos, síntomas comunes, cómo reportar errores útilmente.
  • docs/PLAYWRIGHT_PATTERNS.mdpatrones probados (SPA detection por URL + DOM, OTP en inputs maxlength=1, selectores defensivos, screenshots de debug, validación contra API tras mutaciones, SessionExpiredError para abortar early en bulks). Incluye plantilla para scripts nuevos y checklist de revisión.

Capa agentica MCP

El proyecto expone un servidor MCP (stdio) en mcp_server/ que Claude Code consume automáticamente vía .mcp.json en la raíz. Da acceso tipado a cuentas, contactos, opps, syncs y al runner genérico run_script que cubre los ~60 scripts no expuestos como tool dedicada. Defaults seguros: toda tool mutadora arranca con apply=False y requiere confirm_token="I-HAVE-USER-CONFIRMATION" para aplicar — forzando confirmación humana explícita. Los runs con apply=True quedan en script_audit y son reversibles desde el dashboard.

  • Guía user-facing (cómo aprovecharlo desde Claude Code, recetas y prompts): docs/GUIA_AGENTICA.md.
  • Catálogo técnico (contrato, tools, troubleshooting): docs/AGENT_TOOLS.md.
  • Manifest autogenerado: generated/agent/tools_manifest.json. Auditoría de salud: python scripts/audit_agent_readiness.py.

Reglas críticas que no son obvias

  1. El CSV Bucéfalo - Mesa de control - API Tokens - MP.csv y los tokens son secretos. No los imprimas en logs ni los persistas en SQLite.
  2. Paginación GHL obligatoria: contactos con meta.nextPageUrl/startAfter, oportunidades con POST /opportunities/search. Nunca uses offset pagination ni GET /opportunities/. Detalle completo en AGENTS.md.
  3. Marca: Bucéfalo es nuestro CRM. Aunque GHL aparece en el código por necesidad técnica, no lo menciones como producto en interfaces de usuario o documentación destinada a clientes.
  4. Pipelines sintéticos: si GHL devuelve [] de pipelines pero hay oportunidades con pipelineId, sync_engine los reconstruye desde las etapas — no es bug, es fallback.
  5. Antes de modificar GHL: prefiere correr el script en modo --dry-run y revisar el resumen de auditoría antes de aplicar.
  6. Registro de casos (docs/casos/): bitácora de operaciones/investigaciones reales, optimizada para recall del agente. Antes de investigar un síntoma nuevo, grep ahí por el error/síntoma (quizá ya está resuelto). Después de mutar Bucéfalo o cerrar una investigación con causa raíz, registra el caso (copia docs/casos/_PLANTILLA.md, agrega fila a docs/casos/INDEX.md). Es complemento de los PLAYBOOK_* (teoría) y la memoria (hechos atómicos).