Files
MP-Manager/AGENTS.md
T
urieljareth 2a37a4ffbf Añade launchers de macOS/Linux con un click y hace runtime_control cross-platform
Equivalentes .command (doble-clic en Finder) de los .bat de Windows:
- setup_mac.command: bootstrap con un click (detecta Python 3.10+, crea .venv,
  instala requirements + Chromium de Playwright, copia .env.example -> .env).
- start/stop/restart/start_persistent_profile.command: espejo de los .bat,
  lanzan el server con nohup usando el python del .venv.
- mp_common.sh: helper compartido (raíz, venv, banners).

runtime_control.py ahora es cross-platform (IS_WINDOWS): lsof/ps/pgrep/kill en
POSIX, netstat/PowerShell/taskkill en Windows. _kill_tree_posix mata el árbol
padre+worker de uvicorn con SIGTERM.

.venv/ añadido a .gitignore. Docs actualizadas (CLAUDE.md, AGENTS.md,
PLAYWRIGHT_SESSION.md).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 15:02:47 -06:00

10 KiB

AGENTS.md

Capa agentica (MCP): para uso desde Claude Code u otros clientes LLM, este repo expone un servidor MCP stdio en mcp_server/ con tools tipadas (defaults dry-run, confirm_token para aplicar, rollback vía script_audit). Ver docs/AGENT_TOOLS.md para el catálogo y recetas. Manifest navegable en generated/agent/tools_manifest.json.

Commands

  • Install deps with python -m pip install -r requirements.txt.
  • Run the local app with python main.py; it serves FastAPI/Uvicorn at http://127.0.0.1:8000 with reload enabled.
  • On Windows, start.bat runs python main.py in a new window and opens the browser; stop.bat kills any process using port 8000.
  • On macOS/Linux, the double-clickable .command files mirror the .bats: run setup_mac.command once to bootstrap a .venv (Python 3.10+) and deps, then start.command / stop.command / restart.command / start_persistent_profile.command. They delegate the safe preflight/stop logic to the now cross-platform runtime_control.py.
  • There is no test, lint, or formatter config in this repo. For a syntax-only check, run python -m py_compile main.py db.py ghl_client.py sync_engine.py script_runner.py and add specific scripts as needed.
  • Run focused utility scripts directly, for example python scripts\mp_contact_search.py <query> or python scripts\mp_opportunity_search.py <query-or-status>.
  • Global dashboard sync parallelism is controlled with SYNC_ENGINE_MAX_WORKERS; default is 12, hard maximum is 20. It affects the Sincronizar Todo button and processes multiple GHL locations in parallel.
  • Dashboard batch parallelism is controlled globally with SCRIPT_RUNNER_MAX_WORKERS; default is 4, hard maximum is 20. Higher values can increase GHL 429/timeout risk because script subprocesses do not share in-memory rate-limit state.

App Wiring

  • main.py is the app entrypoint; API routes call db.py, sync_engine.py, and script_runner.py.
  • templates/index.html plus static/js/app.js and static/css/style.css are the single-page dashboard UI.
  • db.py owns the local SQLite schema at generated/data/mp_manager.sqlite (path comes from paths.DB_PATH); sync writes replace per-location contacts, pipelines, and opportunities inside transactions.
  • sync_engine.py reads Bucéfalo - Mesa de control - API Tokens - MP.csv at startup/sync time. The CSV must include Location_ID, Nombre, and API_token; tokens are cached in memory and are not stored in SQLite.
  • The main brand account is hard-coded as location GbKkBpCmKu2QmloKFHy3; all other CSV locations are treated as branches.

Generated Files

All dynamic outputs live under generated/ and are sourced from paths.py. Never hardcode disk paths in new code — import from paths (or via scripts/common.py, which re-exports them). Layout: generated/data/ (SQLite), generated/reports/ (audit/dup/drift/coverage outputs), generated/exports/ (downloads served by /api/exports/), generated/logs/ (errors.jsonl + script_runs/), generated/migrations/ (pre-destructive snapshots), generated/browser/ (Playwright session, profile, screenshots), generated/runtime/ (server_info.json, last_mode, in-flight bulk batches), generated/_archive/ (legacy). The whole tree is gitignored and megaignored.

GHL API Gotchas

  • ghl_client.py is the executable source for GHL behavior: base URL https://services.leadconnectorhq.com, API version header 2021-07-28, and bearer-token auth.
  • Keep the per-token rate limiting in GHLClient._wait_for_rate_limit; global sync concurrency must use SYNC_ENGINE_MAX_WORKERS instead of hardcoded worker counts.
  • Contacts paginate with meta.nextPageUrl and startAfter; do not replace this with offset pagination.
  • Opportunities must be fetched with POST /opportunities/search; GET /opportunities/ is documented here as returning empty/zero results.
  • If GHL returns no pipelines but opportunities have pipeline IDs, sync creates synthetic pipelines from opportunity stages instead of failing.

GHL Pagination & Full Dataset Retrieval

  • Contactos: usar la paginación cursor-based de GHL con meta.nextPageUrl, startAfter y, cuando aplique, startAfterId. No usar offset pagination. Continuar solicitando páginas hasta que no exista siguiente cursor/URL o hasta alcanzar un límite explícito del script (--limit, --max-contacts, etc.).
  • Oportunidades: usar exclusivamente POST /opportunities/search por locationId. Si se requiere un dataset completo, paginar/iterar la búsqueda hasta agotar resultados o alcanzar un límite explícito. No usar GET /opportunities/ para escaneos porque puede devolver vacío/cero resultados.
  • Acumulación segura: cada página debe agregarse a una colección global, deduplicando por id para evitar repetidos entre páginas o reintentos.
  • Protección anti-loop: todo paginador debe controlar cursores/URLs ya vistos, páginas vacías repetidas o límites máximos razonables para evitar ciclos infinitos.
  • Auditorías profundas: antes de comparar, sincronizar o corregir datos, el script debe haber consolidado todas las páginas necesarias de contactos/oportunidades para no dejar datos fuera del análisis.
  • Resumen final: los scripts que paginan deben reportar total de páginas consultadas, total recibido, total único procesado, duplicados descartados y si se cortó por límite.

Dynamic Schema & Custom Fields Mapping

Para consultar y mapear campos personalizados cuyos IDs varían entre diferentes sucursales de GHL, se debe utilizar el siguiente flujo de API (ambos usando la cabecera Version: 2021-04-15):

  1. Obtener Catálogo de Objetos:
    • Endpoint: GET https://services.leadconnectorhq.com/objects/?locationId={locationId}
    • Detalle: Requerido para inicializar y autorizar la sesión de consulta de esquemas de metadatos en GHL para esa ubicación.
  2. Obtener Esquema por Objeto (Schema by Key):
    • Endpoint: GET https://services.leadconnectorhq.com/objects/{objectKey}?locationId={locationId}
    • Parámetro {objectKey}: Usar contact o opportunity.
    • Detalle: Devuelve el listado completo de campos (estándar y personalizados). Permite mapear de forma dinámica campos por su nombre (ej. "Canal de Origen" o "Fuente de Prospecto") a su correspondiente ID dinámico en esa sucursal para lectura o actualización segura.

Business Logic & Field Correlations (Monte Providencia Rules)

Derivado de DOCUMENTACIÓN de Monte Providencia.md, estas reglas rigen el comportamiento de los scripts, la integridad de los datos y el mapeo en GHL:

1. Flujo de Sincronización y Prioridades

  • Sincronización de Contactos: Bidireccional entre la cuenta de Marca principal (GbKkBpCmKu2QmloKFHy3) y las sucursales. Cualquier creación/modificación en un lado se sincroniza al otro.
  • Sincronización de Oportunidades: Unidireccional de Sucursal a Marca (la sucursal tiene prioridad). Las modificaciones de oportunidades en la cuenta de marca no se sincronizan hacia las sucursales.
  • Multi-Oportunidades: Un mismo contacto puede tener más de una oportunidad (múltiples intentos de empeño).

2. Campos Requeridos para Business Intelligence (BI)

Cada contacto u oportunidad debe poblar consistentemente:

  • Sucursal (y campo TIENDA) alineados con el "verificador de sucursales".
  • Canal de Origen (e.g., FORMULARIO, FACEBOOK, WHATSAPP, LLAMADA, INSTAGRAM, SUCURSAL).
  • Fuente de Prospecto (e.g., CLIENTE CONOCIDO, SUCURSAL, PROSPECCIÓN, REFERIDO, ALIANZA, EVENTO ESPECIAL, LEAD DIGITAL).

3. Reglas de Validación de Origen/Fuente

  • LEAD DIGITAL siempre proviene de un canal digital (Canal de Origen: Formulario, Facebook, etc.).
  • Si el Canal de Origen es SUCURSAL, la Fuente de Prospecto no puede ser LEAD DIGITAL.
  • El tag "sucursal" en contactos implica creación manual en sucursal, a menos que el campo source nativo de GHL indique lo contrario (un origen digital).

4. Correlación de Datos de Vehículo

  • Para prospectos con fuente LEAD DIGITAL, el campo Vehículo de la oportunidad debe construirse concatenando los campos personalizados del contacto: "Marca del Vehículo" + "Versión del Vehículo" + "Año del Vehículo".
  • En oportunidades creadas manualmente, el campo de vehículo de la oportunidad puede diferir de los datos del contacto.

5. Pipelines y Oportunidades Huérfanas

  • Toda oportunidad debe residir en un pipeline activo válido (comúnmente llamado "Standar").
  • Oportunidades huérfanas (sin pipeline válido) se deben reubicar según coincidencia de nombre de etapa (stage) o marcar para revisión.

Scripts And Data Safety

  • script_runner.py exposes only scripts listed in SCRIPTS_METADATA; add metadata there when adding a dashboard-runnable script.
  • Most scripts read mp_manager.sqlite from the repo root and expect a prior dashboard sync. Some scripts are live GHL mutators (fix_*, migrate_*, move_*, update_*, sync_contact_*); prefer read-only audit/search scripts first unless the user explicitly asks to modify GHL data.
  • Treat Bucéfalo - Mesa de control - API Tokens - MP.csv and any printed API tokens as secrets.

Case log (registro de casos)

  • docs/casos/ is the chronological log of real operations/investigations on Bucéfalo, written for agent recall (dense, exact commands, literal ids/errors). It complements the docs/PLAYBOOK_* files (timeless theory) and the agent memory (atomic facts).
  • When to add a case: after any Bucéfalo mutation (with run_id/snapshot) OR after closing a non-trivial investigation that reached a root cause (even with no mutation).
  • How: copy docs/casos/_PLANTILLA.mddocs/casos/YYYY-MM-DD-<slug>.md, fill it, add the row to docs/casos/INDEX.md, and link the related memory with [[slug]].
  • Before investigating a new symptom, grep docs/casos/ for the error/symptom — it may already be solved.