Gotchas: Dónde Vive La Lógica No Obvia
Las trampas que cuestan horas porque no están donde uno las busca. Si vas a depurar o explicar algo de estos temas, lee esto primero.
Lógica Que Vive En Zoho (no en el repo Node)
Mucha lógica de negocio no está en el código TypeScript sino en functions Deluge de Zoho Creator, que se editan en su consola web y no se deployan desde este repo.
- Stripping del sufijo
-Nde sigmas escaneados (ej.S1424855-2→S1424855): el cliente (BarcodeScanScreen.tsx,CheckpointsScreen.tsx) y el backend Node pasan el código sin tocar. Quien quita el-Nes Zoho, envalidateCheckPoint/generateCheckPoint. Si reaparece "no se puede asociar tarea con guión" o "el checkpoint no crea la tarea con sufijo", mira primero la function de Zoho, no la normalización en Node.- ⚠️ No agregues un
replace(/-\d+$/, "")ennormalizeScanIddel backend: duplicaría lógica que ya vive en Zoho y rompería casos donde el sufijo sí debe preservarse.
- ⚠️ No agregues un
- Schedules y notificaciones, intake de PARED/SIGMA, SCAN, caja/arqueo: viven como forms, functions y schedules de Zoho. El repo
apps/zoho-appes un espejo, no la fuente de deploy.
Regla mental: si una transformación "debería" estar en el backend y no la encuentras, asume que está en Zoho antes de concluir que falta.
El .env Local Apaga Cosas A Propósito (y no refleja prod)
El .env.example trae defaults pensados para desarrollo local, no los valores de producción. En particular, varios comportamientos pesados o que "regeneran" cosas vienen apagados a propósito para que probar en local no dispare trabajo de fondo.
ROUTE_ENGINE_SESSIONS_ENABLED=falseen local es intencional. Evita que la optimización de ruta se re-dispare/regenere cada vez que cambia el set de tareas mientras desarrollas. En producción estátruey es el path que gobierna el orden de ruta (ver orden de ruta).- El engine sigue el mismo criterio: en su
.env.examplevienen enfalsepor defaultROS_ASYNC_OPTIMIZE_ENABLED,ROS_ETA_SYNC_ENABLED,ROS_AUTH_ENABLED,ROS_RATE_LIMIT_ENABLED— trabajo de fondo y controles que no quieres activos mientras pruebas en local.
Consecuencia para el dev: el valor productivo de estos flags vive solo en Railway/Cloudflare. No infieras el comportamiento de prod desde el .env local — verifica el flag en Railway antes de concluir "esto está apagado". Y si necesitas reproducir prod en local (p. ej. ver la reoptimización de ruta), actívalo a mano en tu .env.
El Orden De Ruta Lo Decide La Sesión, No El GPS
El engine de rutas (apps/engine-backend, VRPTW/OR-Tools) se invoca desde drivers-backend por dos caminos que escriben el mismo tasks.numeroEntregaRuta:
engineSession/applyTaskAssignment— es el que gobierna el orden en producción. Optimiza desde un depot FIJO hardcodeado (8.9824,-79.5199, zona Albrook/Calidonia — no el GPS del conductor), conshift_start: "06:00"hardcodeado, minimizando el lazo completo (no "lo más cercano primero"), sin ventanas y con todas las tareas comocritical.- Legacy
/api/route/optimizesí usa GPS + hora real + ventanas, pero es código muerto en la app desplegada (no lo llama ninguna pantalla). Riesgo latente, no compite hoy.
Consecuencias al depurar:
- "¿Por qué lo mandó lejos teniendo algo cerca?" → porque el orden se calcula desde el depot fijo y minimiza el recorrido total; una parada cercana al conductor puede ser óptima como #4.
- "¿Por qué los ETA salen ~6:57/7:00 AM?" → por el
shift_start06:00 hardcodeado + re-sync que no re-ancla el inicio. No es zona horaria. - El reoptimize de sesión se dispara por eventos de tarea (assigned/completed/rescheduled) y un cron de reconciliación cada 2 min solo ante drift del set de tareas. El GPS nunca reordena.
Base De Datos Compartida
core-backendydrivers-backendusan la MISMA Postgres (la "Main Dashboard DB"), ambos porDATABASE_URL. No hay DB separada de drivers en runtime.- El schema lo posee core (
packages/core-shared/src/schema.ts); drivers tiene un espejo (shared/schema.ts).core-backend ... checkcorrecheck-schema-mirror.tspara cazar drift. db:pushestá deshabilitado en drivers a propósito. Aplica el schema solo desde core-backend.MAIN_DB_URL/DRIVERS_DB_URL(en el.env.examplede drivers) son solo del script históricoscripts/migrate-to-main-db.ts, no de runtime.- Migraciones = deuda técnica, por dos carriles (
db:generate/db:migrateestán en desuso). Lo aditivo (columnas/índices) se aplica solo en cada deploy vía una lista idempotente encore-backend/server/routes.ts(auto-migrator). Lo no-aditivo va como SQL fechado a mano enmigrations/manual/, aplicado a prod conrailway run -- psql … -f. Procedimiento completo en Setup → Migraciones.
Pendiente recomendado: una DB de dev
Hoy el desarrollo local apunta a producción (con cuidado de no escribir), lo cual es frágil. Una opción razonable: una Postgres de dev (efímera o con dump anonimizado de prod) a la que apunten ambos backends vía DATABASE_URL, y aplicar el schema desde core-backend (db:push). Como comparten DB, un solo DATABASE_URL de dev sirve para los dos.
Dos "dashboards" De dash-web
core-backend puede bundlear dash-web al construir el server (queda embebido en el servicio de Railway), pero el dashboard de producción que usan los operadores es el Cloudflare Worker (frontend-dash-rnb-logistics-prod), que se deploya manualmente.
No asumas que "el dash de prod es el que embebe core-backend": si mergeas un cambio de dash-web y no corres deploy:prod, el frontend que ven los usuarios no cambia, aunque el backend ya se haya redeployado. Es el olvido clásico de cierre de ticket.
El Mapa De drivers-app Es Server-Rendered (y sin telemetría)
El mapa de la app de conductores no es client-side: GoogleMapsWebView.tsx hace POST /api/map al drivers-backend (Railway), que devuelve HTML con Google Maps + markers renderizado en un WebView.
- La pantalla "Mapa no disponible" se dispara solo cuando ese POST falla (red caída, respuesta no-2xx, cold start/502 de Railway, 520/521 de Cloudflare, o
GOOGLE_MAPS_KEY_DRIVER_APPvacío → 500). - Punto ciego: el
catchsolo haceconsole.error; no llama aSentry.captureExceptionni reintenta. Por eso no verás excepciones de mapa caído en Sentry — el único rastro es si el conductor lo reporta a mano. Distinto del flood demissing coordsy de "No hay tareas para hoy".
Worktrees Sin node_modules
Los .claude/worktrees/* no tienen node_modules. Correr pnpm install ahí satura la máquina y rompe el shell. Desarrolla en el worktree, prueba en el checkout principal. Detalle y limpieza post-merge en Flujo de trabajo.
Resumen De "Mira Primero Aquí"
| Síntoma | Mira primero |
|---|---|
Sigma con -N no asocia / checkpoint no crea tarea | Function de Zoho (no Node) |
| Un flag local está apagado / difiere de prod | Default de dev a propósito; valor real en Railway/Cloudflare |
| Orden de ruta "raro" / ETA ~7 AM | Path de sesión del engine (depot + shift_start fijos) |
| Cambié schema y drivers no ve el cambio | Schema se aplica desde core; revisa mirror/drift |
| Voy a cambiar una tabla / el schema | Aditivo → auto-migrator en routes.ts; no-aditivo → migrations/manual/ + railway run psql (Setup → Migraciones) |
| Cambié dash-web y prod no cambió | Faltó el deploy:prod a Cloudflare |
| "Mapa no disponible" sin nada en Sentry | POST /api/map falló; no hay telemetría |
| Schema/lógica que "falta" en el backend | Probablemente vive en Zoho |