Hub

2026-05-26

martes · 26 de mayo de 2026

2026-05-26

Sesión: machine-parity — retomar desde el estado del 22-may

Pidió Sergio

"¿Qué sigue en el proyecto machine-parity?" — y al ver el panorama autorizó arrancar en el orden propuesto: primero Docker + script de tooling, después clonar los 6 repos faltantes.

Hecho

  1. Verificación de estado real vs el plan del 22. Inventario silencioso desde la última sesión: gh 2.92.0 ya estaba instalado (sin saber cuándo); wireguard-tools y wrappers Docker seguían faltando; aprende-ingles, es-antenas-new, tareas-hijo, hub-web-viewer aparecieron en ~/code/ post-22 sin documentarse aquí.

  2. #150 (tooling base) cerrado. Docker Desktop ya tenía la integración WSL activa (server 29.4.3 visible desde el host). Primer intento de Sergio: sudo apt /tmp/parity-tooling.sh falló con "Unsupported file" (typo apt por bash). Segundo intento: sudo bash /tmp/parity-tooling.sh corrió completo — wireguard-tools 1.0.20210914 + wrappers Docker en /usr/local/bin/ (composer 2.9.8 con PHP 8.5.6 dentro del image; mysql 8.4.9). Todos validados desde el host.

  3. #154 (clonar repos) cerrado. 6 clones en paralelo: amadeus y monitoreo-collector de electrosystems-mx, jm-admin/jm-checador/jm-contabilidad/medicinas de sevaor. ~/code/ final: 16 directorios. Los otros 2 del plan original (aprende-ingles, es-antenas-new) ya estaban clonados de antes.

  4. #159 (regla "verificar última versión") cerrado. La memoria feedback_keep_repos_synced (creada entre el 22 y hoy) ya cubre la regla — fetch+status al inicio del hub y antes de tocar cualquier ~/code/<slug>/. No requiere formalización adicional en ANTIGRAVITY.md salvo preferencia futura.

  5. Documentos del hub actualizados atómicamente. projects/machine-parity.md (frontmatter updated: 2026-05-26, 3 tareas cerradas con fecha, bitácora del día) + PENDIENTES.md (sección machine-parity recortada de 10 a 7 abiertos + 1 cerrado consolidado).

Próximo paso

Cuando Sergio entre a la laptop del trabajo, generar el comando del tarball cifrado (#151) con whitelist explícita (.ssh/config + llaves no-id_rsa_es + .env que él decida incluir) y traerlo a la PC personal. Eso desbloquea #153 y permite arrancar #157-#158 (WireGuard unificado) una vez se aclare el servidor WG.

Pista para #156

En la org electrosystems-mx existen los repos docs y documentacion. Confirmar con Sergio si alguno es el remote actual de ~/agy/electrosystems/ o si se generó después y vive solo local en la laptop trabajo.

Hallazgo lateral (no acción)

~/code/hub-web-viewer/ apareció en la PC personal entre el 22 y hoy. No estaba en el plan original de machine-parity — es el viewer propio del hub. Mencionado en la bitácora del proyecto, sin requerir acción.


Sesión holbox#163 prendido en prod + #245 cerrado + visual refresh + #165 pre-research entregado v2

Foco

Sesión larga sobre holbox. Arrancó con sync del repo (Sergio ya había trabajado #163 + #164 + incidente TR-90 + mejoras de inventario en la otra máquina) y terminó con un pre-research grande de CRM/IA conversacional cuyo shape cambió en medio por contexto adicional.

Hecho

  1. Pull a ~/code/holbox — 5 commits jalados (2035963 #163 flag separar_en_top_skus + 73930c4 toggle TOP SKUs + bbdd35e #164 historial compras + e085b89 filtro inventario + 310aaea badge categoría en fila). Verificación de bitácoras del proyecto y PENDIENTES.md confirmó que el hub ya reflejaba todo ese trabajo. Encontré cambios huérfanos de una sesión madrugada (cerraron #125 hub-portability E2E) sin commit; el hook auto-commit los capturó como 6c54058.

  2. Flag #163 prendido en prod por Sergio (/categorias/9/edit — cat "Holbox I.A TR-90" con separar_en_top_skus=true). Cierre operativo del #163.

  3. #245 CERRADO end-to-end — editar líneas de traslados pendientes desde la UI. Implementación completa: migration editado_at + FK editado_por_user_id, Traslado fillable/cast/relación editor(), TrasladoController::update() con abort_if(!isAuth, 403) + detalles()->delete() y recreate en transacción + setea auditoría, Route resource extendida a 'update', Show.vue con modo edición inline (banner ámbar "no afecta inventario hasta enviar", tabla editable + quitar línea + selector "Agregar producto" filtrando ya usados + textarea de notas). Estados enviado/recibido/cancelado bloqueados. 11 tests Pest verdes, 54 assertions. Suite 321/28 (los 28 son pre-existentes, ninguno mío). Pint pass. Commit 3b4536b push OK, GHA 26433880024 ✅ success. Resuelve operativamente el hotfix manual del 25-may del SKU ST2801C2 (Alom). Decisiones tomadas en modo auto sin pausar: UI inline en lugar de pantalla /edit aparte, endpoint atómico PUT único en lugar de granular, auditoría incluida (barata + útil), cancelar traslado completo fuera de scope.

  4. Follow-up visual del #245 — Sergio notó que Show.vue desentonaba contra Index/Ventas/Compras. Reescrito completo manteniendo lógica (11 tests siguen verdes): max-w-7xl, header con ArrowLeftIcon + título text-2xl tracking-tight, card cabecera con ArrowsRightLeftIcon en burbuja primary + ruta visual Origen→Destino con MapPinIcon, chip de estado con dot pulsando, timestamps con iconos color-coded, KPIs Líneas/Unidades reactivos también en modo edición, tabla rounded-2xl divide-y con SKU chip primary, modo edición con XMarkIcon para quitar y botones bg-primary-600/bg-emerald-600 con iconos heroicons (se quitaron emojis ✈/✓/✎ y colores hardcoded). Commit 4824d85, GHA 26434508889 ✅ success.

  5. #165 pre-research CRM v1 entregado en projects/holbox-crm-research-2026-05.md — primera versión arrancó con AskUserQuestion (4 preguntas a Sergio) + SSH read-only a prod (659 clientes, 612 con email, 1 opt-out WA, 769 ventas históricas totales, 23 users, 6 sucursales) + WebFetch pricing en vivo (Kommo $15/$25/$45, Respond.io $79+, Zoho free 3 users, etc.) + análisis del bloqueador WABA (un número solo permite UNA Cloud API). Recomendación primaria v1: piloto Kommo Base con número dedicado distinto al WABA actual.

  6. #165 reescrito v2 tras contexto adicional de Sergio. Sergio aclaró: (a) clientes deben responder al MISMO número del que reciben tickets/recordatorios, (b) IA conversacional con prompt editable (qué decir, qué ofrecer, qué tratar de vender, qué promos), (c) handoff humano por escalado (IA intenta primero, escala si no puede), (d) diseño portable para Joyerías/Greco/Deportes-Campeón. Esto invalida la opción "número dedicado" y descarta HubSpot/Zoho/Bitrix24/Pipedrive/Salesforce. Recomendación primaria cambió a construir in-house en Holbox módulo Conversaciones+IA+handoff portable ($45-55k MXN upfront + $5-20 USD/mes Anthropic Antigravity Haiku 4.5, 5-7 semanas, break-even vs Kommo Pro a ~25 meses, ~12-14 meses si se adopta en 1 cliente más). Top 2 Kommo Pro ($90 USD/mes + migración WABA $3.5-5k one-time, 2 semanas pero recurrente perpetuo + lock-in). Doc completo con arquitectura técnica de 10 puntos, plan de 3 fases (Fase 1 inbox sin IA, Fase 2 IA + escalación, Fase 3 multi-tenant), matriz comparativa con TCO a 18/24 meses, riesgos y supuestos.

  7. 5 pendientes derivados del #165 abiertos con IDs #255-#259 (manteniendo IDs cuando se reasignaron tras v2):

    • #255 — pasar doc v2 a Aarón + 4 preguntas clave (in-house vs Kommo, volumen WA inbound, preguntas comunes para prompt, timeline).
    • #256 💰 — Fase 1 inbox sin IA ($17.5-21k MXN).
    • #257 💰 — Fase 2 IA + escalación ($14-17.5k MXN).
    • #258 💰 — Fase 3 multi-tenant opcional ($10.5-14k MXN).
    • #259 💰 — ALTERNATIVO Kommo Pro migración ($3.5-5k MXN one-time).
  8. Hub actualizado atómicamente con cada cierre. projects/holbox.md con bitácoras 2026-05-26 (4 entradas — pull, #245, follow-up visual, #165 v1+v2), frontmatter updated: bumpeado, last-fix reescrito. PENDIENTES.md con #245 cerrado, #163 implícitamente cerrado por Sergio, #165 cerrado con doc derivado, sección holbox completamente reescrita, header con info reciente. Next-id 255→260 después de mis asignaciones.

Próximo paso

Sergio le pasa a Aarón el TL;DR del doc CRM + tabla de costos a 18/24 meses + la pregunta clave "¿$45-55k MXN one-time con IP propia y 5-7 semanas, o $1,800 MXN/mes recurrente y operativo en 2 semanas?". Su respuesta determina si abrimos las Fases in-house (#256-#258) o la migración a Kommo (#259). El research en sí no se facturó; los pendientes que abra Aarón sí.

Decisiones notables tomadas en modo auto (sin pausar a preguntar)

  • #245 con UI inline en Show.vue (no pantalla separada /edit).
  • #245 con endpoint atómico PUT único (no granular).
  • #245 con auditoría incluida (barata + útil para reclamos).
  • #165 pre-research presentado primero, no deep-dive — Sergio puede pedir profundizar si Aarón pica.

Colisiones de IDs detectadas y resueltas

  • Sesión paralela aprende-ingles ya había usado #253; sesión paralela hub-web-viewer usó #252 y #254. Los pendientes del CRM se asignaron a #255-#259 en lugar de #252-#255 que había planeado originalmente. next-id quedó en 260.

Hallazgo lateral (no acción)

3 tests pre-existentes en TrasladoControllerTest.php con comentarios drifteados ("controller tira abort_if 422" cuando el código real retorna redirect()->back() 302). No los toqué — no son míos y existían antes. Si en algún momento Sergio quiere alinear enviar() y recibir() al mismo patrón abort_if(403) que usa mi update(), son ~30 min de trabajo.


Sesión: tareas-hijo#184 Fase 1 bloque 2/4 (LiveView /hijo + /papa con PubSub)

Pidió Sergio

"¿Qué sigue en el proyecto tareas-hijo?" → al ver el estado autorizó arrancar el bloque 2/4 del #184. Después de cerrarlo y planear el 3/4, decidió cerrar sesión sin arrancar el siguiente.

Hecho

  1. Bloque 2/4 cerrado completo. 4 archivos nuevos en ~/code/tareas-hijo/ (Sergio tecleó 100% — modo coaching estricto):

    • lib/tareas_hijo/tareas.ex — context con PubSub helpers (subscribe/1 + topic/1 privado + broadcast/2 privado) + 3 fns BD (lista_del_dia/2 con from(... join, preload con binding) evitando N+1; obtener_o_generar_del_dia/2 con materializar_dia/2 privado usando Repo.insert_all/3 + on_conflict: :nothing sobre el unique compuesto (def_id, user_id, fecha) para race protection entre pestañas; marcar_hecha/1 después refactorizado a toggle_estado/1).
    • lib/tareas_hijo/cuentas.ex — mock helpers get_hijo!/0 + get_papa!/0 (pre-auth para Fase 1).
    • lib/tareas_hijo_web/live/hijo_live.exmount con connected? guard + subscribe, handle_event("toggle_estado", ...) que NO toca socket (PubSub se encarga del refresh — único path de update), handle_info({:tarea_actualizada, ...}). Render con <li :for>, class={[lista]}, dark mode pareado.
    • lib/tareas_hijo_web/live/papa_live.ex — read-only suscrito al topic del HIJO (no del propio user), sin handle_event, checkbox como <div> decorativo, contador "X de Y hechas" via defp hechas_count/1 local.
    • Edit router.ex — 2 rutas live (/hijo, /papa).
  2. Bug atrapado mid-sesión y resuelto. Sergio reportó "me aparecen 2 tareas marcadas y no las puedo desmarcar". Diagnóstico: las 2 marcadas eran residuo del testing IEx de la entrega 2.2 (esperado, quedaron persistidas) + disabled={tarea.estado == :hecha} impedía desmarcar (por diseño de marcar_hecha/1 solo pendiente→hecha). Fix: refactor a toggle_estado/1 (pendiente ↔ hecha) con pattern matching case instancia.estado do :pendiente -> ... :hecha -> ... end. Un solo cambio resolvió ambos problemas + aterrizó un patrón nuevo (handle_event que decide attrs según estado).

  3. Validación E2E en 3 pestañas paralelas. /hijo × 2 + /papa abiertas — toggle se refleja al instante en las otras dos sin recargar. Primera vez que Sergio ve PubSub aterrizado en código real tras la sesión teórica del mapa Phoenix C1-C10 (2026-05-23 noche).

  4. Documentación atómica en el hub. projects/tareas-hijo.md (frontmatter updated: 2026-05-26, bitácora del día con decisiones técnicas + gotchas, "En progreso" actualizado al bloque 3/4) + PENDIENTES.md (encabezado de tareas-hijo + #184 actualizado a "EN PROGRESO 2/4" + "Resuelto 2026-05-26 [#184 bloque 2/4]" entrada larga + "Cerrado reciente" con entrada nueva + header "Última actualización"). Commit hub 053d526 pusheado a origin/main.

  5. Plan del bloque 3/4 acordado pero NO ejecutado. Decisiones tentativas capturadas para próxima sesión (ver "Próximo paso").

Próximo paso

Arrancar bloque 3/4 del #184 — LiveView /admin con CRUD tareas_definicion. Decisiones ya tomadas con Sergio (capturadas también en projects/tareas-hijo.md):

  • Layout: single-page (tabla arriba + form abajo siempre visible, no rutas separadas).
  • Form: LiveComponent separado (DefinicionFormComponent), no en el mismo módulo del LV padre.
  • Sin "Eliminar": en su lugar toggle Activa/Inactiva. La columna activa ya existe y cumple la función — hard delete rompería histórico vía on_delete: :delete_all que pusimos en tareas_instancia (necesario para puntos en Fase 4).
  • Sin broadcast cross-LV de cambios de definiciones por ahora (Sergio es el único admin; las instancias del día ya están materializadas; Oban en Fase 2 tomará control).
  • Patrón Phoenix 1.7+: LiveComponent emite notify_parent({:saved, definicion}) y el LV padre matchea en handle_info.
  • Reusar <.input>, <.label>, <.button>, <.error> de TareasHijoWeb.CoreComponents (generados por mix phx.new).

Entrega 3.1 ya está armada en el contexto de chat pero Sergio NO la tecleó — 5 funciones nuevas a agregar en lib/tareas_hijo/tareas.ex (lista_definiciones/0, change_definicion/2, crear_definicion/1, actualizar_definicion/2, toggle_activa/1). Cuando Sergio retome, arrancar con eso. Modo coaching estricto sigue vigente.

Pendiente del lado de Sergio (no urgente)

  • Commit + push de los 5 archivos del proyecto en ~/code/tareas-hijo/ (4 nuevos + 1 edit del router). Modo coach: yo no commito en el repo del proyecto. Comando sugerido capturado en el chat. Si Sergio cierra el browser sin commitear, los cambios están en working tree y se recuperan al volver.

Hallazgos / gotchas técnicos capturados (para retomar fluido)

  • connected?(socket) guard en mount: mount corre 2x (HTTP + WS). Efectos colaterales (subscribe, GenServer.cast) van detrás del guard para correr solo en la pasada WebSocket.
  • from(... join: d in assoc(i, :rel), preload: [rel: d]): reusa el join para preload sin N+1. Si fuera preload: :rel (sin binding) serían 2 queries.
  • Repo.insert_all/3 con schema (no string): sí castea Ecto.Enum (átomo→string). NO corre changesets, NO setea timestamps auto. Combinar con on_conflict: :nothing + conflict_target: [...] para upserts idempotentes.
  • ^var in column con array Postgres: se compila a $1 = ANY(column), operador nativo eficiente.
  • Repo.update descarta preloads: la struct devuelta solo tiene los campos actualizados. Hay que re-pegar manualmente (%{updated | rel: original.rel}) si el caller espera la struct preloaded (caso típico: broadcast con la struct para que el receptor renderice).
  • HEEx directives modernas: <li :for={x <- @lista}>, <span :if={cond}>, class={[list-with-nil-filtered]}. Sustituye <%= for %>/<%= if %> viejo.

Sesión: hub-web-viewer — Fase 2 completa (Pagefind + Log + Filtros antigüedad + Métricas dashboard)

Resumen

Cuatro features de Fase 2 entregados y live en CF, más un fix estructural del loader y una autorización permanente nueva. El proyecto pasa de "Fase 1 LIVE" a "Fase 2 funcionalmente completa" (solo queda migración opcional Default UI → Component UI de Pagefind).

Entregas

  1. #250 — Pagefind en header global (commit d8e999d). Search box con dropdown de resultados, indexación 56→68 páginas, WASM ES auto-detectado, dark mode parejo (sky/slate, <mark> tabaco). Pre-fix de schema: status enum → z.string() + statusFallbackClass para badges seguros, STATUS_ORDER con statusRank(). Gotcha glob: electrosystems-network-map/README.md excluido (después reemplazado por loader robusto, ver más abajo). Setup máquina: Docker node:22-bookworm-slim con -u $(id -u):$(id -g) (esta máquina sin Node 22).
  2. #252 — Vista log diario /log + /log/[date] (commit 989495b). src/lib/log.ts con listLogs/loadLog/renderLog/extractPreview/formatLogDate. Listing agrupado por mes con preview line-clamp-3. Vista individual con nav prev/next. Layout con nav global ≥640px. Gotcha: primer extractor de previews comía underscores (array_valuesarrayvalues), fix a regex que solo limpia **, *, `.
  3. #254 — Filtros antigüedad clickeables en /projects (commit 162e211). 4 chips (Todos / Fresco / 🕒 Stale ≥7d / 🥶 Frozen ≥14d) con conteos en línea, deeplinks shareables via hash URL, JS inline vanilla ~30 líneas con hashchange listener. CSS oculta items + secciones vacías con :has(). Home con link "Filtrar todos →" a /projects/#age=stale. Decisión: filtros client-side via hash sobre rutas estáticas precompiladas (Astro SSG no soporta querystrings).
  4. #259 — Métricas dashboard extendidas (commit 6f3e67e + fix 56687e2). 3 secciones nuevas: ⏰ Próximas fechas (ámbar), 🔒 Disparo condicional (violeta), 📋 Proyectos con más pendientes abiertos (top 8 con badge rose). Helpers extractFechas/extractCondicional (vía extractSection genérico + renderInlineMd con marked.parseInline), countOpenByProject (matchAll de ### [\slug`]`, acumula si slug aparece en múltiples secciones). Top 8 al cierre: es-antenas-new (12) / backups-infra (11) / amadeus/holbox/machine-parity/aprende-ingles (10) / orion-decommission (9) / electro-ia (7). NO incluí "Bloqueadores 🚨" de INDEX.md porque INDEX.md no se mantiene fiable (última update 21-may).

Fix estructural del loader (durante debugging del rebuild)

Sergio reportó que no veía las métricas en CF. Build de CF estaba fallando con InvalidContentEntryDataError en holbox-crm-research-2026-05.md (research doc sin frontmatter de proyecto, traído por el submódulo al rebuild). Mismo patrón que electrosystems-network-map/README.md. En vez de seguir agregando exclusiones manuales una por una, reemplacé el glob() por un loader que pre-escanea hub/projects/, lee los primeros 1.5KB de cada archivo, verifica ^project:\s*\S en el frontmatter, y solo incluye los que pasan. Archivos auxiliares (research, reportes ad-hoc, KB sub-docs) se ignoran silenciosamente. Commit 56687e2 dejó el sitio verde. Exclusión manual previa de electrosystems-network-map/README.md removida (ya redundante).

Autorización permanente nueva

Sergio autorizó push directo a origin/main del viewer en futuras sesiones (memoria nueva [[feedback-hub-web-viewer-push-authorized]]). Implementación: ~/code/hub-web-viewer/.claude/settings.local.json con Bash(git push origin main) en permissions.allow. Gitignored. Comprobado in-session (carga sin requerir restart). El primer push de Pagefind requirió aprobación manual; los siguientes (log, filtros, métricas, fix loader) ya no.

Network gotcha de la sesión

Por la mitad de la sesión, github.com:22 empezó a timeoutear desde esta máquina (¿WSL2 NAT / firewall?). Workaround: pushear via ssh://git@ssh.github.com:443/sevaor/hub-web-viewer.git main:main (GitHub también atiende SSH en 443). Si vuelve a pasar y es persistente, agregar al ~/.ssh/config:

Host github.com
    Hostname ssh.github.com
    Port 443

Eso hace que git push origin main normal funcione transparente aunque port 22 esté bloqueado.

Colisiones de IDs detectadas

Dos veces durante la sesión choqué con IDs ya tomados por otras sesiones paralelas (Sergio trabajando en aprende-ingles + holbox al mismo tiempo): #251 (aprende-ingles), #259 (holbox CRM v2 Kommo). Tuve que reasignar mis cierres a #252 y #254/#259-nuevo (re-asignado). El contador <!-- next-id: NNN --> cambió de 250 → 261 durante el día. Pattern: si vuelvo a ver next-id avanzado entre lecturas, verificar con grep "#NNN" antes de tomar el siguiente.

Estado del proyecto al cierre

  • Fase 1: 100% (Cloudflare Workers + Basic Auth, push del hub triggea rebuild via GH Action, push del viewer triggea auto-deploy CF).
  • Fase 2: 4 de 4 features cerrados. Solo queda #130 migración opcional Default UI → Component UI de Pagefind (~1h, mejor a11y + modal).
  • Fase 3 (opcionales para futuro): calendario "Próximas fechas" visual, heatmap actividad por proyecto, RSS log diario, PWA installable en celular.

Pendientes operativos

  • Ninguno bloqueante. Sergio confirmó visualmente las 4 entregas en producción.

Sesión tarde — monitoreo #191 cerrado

Sergio: "En el proyecto monitoreo: Ataquemos el pendiente #191".

Bug: NotificarLogs.php:686 reventó el cron del 2026-05-23 07:00 con Attempt to read property "enlaces" on null en incluirVecinosDeEnlace. Consecuencia: alertas por correo de todas las caídas del día se quedaron sin enviar.

Diagnóstico (SSH read-only a monitoreo):

  • 824 EstatusMonitoreoDispositivo totales, 21 huérfanos: 16 con MonitoreoDispositivo soft-deleted (ya filtrados por notificable()), 5 con MD vivo pero Dispositivo soft-deleted (no filtrados — causaban el crash).
  • Los 5 vivos son del dispositivo #313 ST Caborca T1-La Gloria C5c, soft-deleted 2026-05-22 15:49:27. IDs 801-805, todos en error desde 2026-05-22 15:45:21.
  • IDs 801 y 802 con ultimo_notificado_at = NULL → entran al bucket "alertas iniciales" → incluirVecinosDeEnlace($e->monitoreoDispositivo->dispositivo->enlaces->pluck('id')) revienta porque dispositivo es null.

Fix (commit e589d67, deploy 88s):

  • app/Models/MonitoreoDispositivo.php: scopeNotificable ahora incluye ->whereHas('dispositivo'). Como Dispositivo usa SoftDeletes, el whereHas respeta el global scope y filtra trashed automáticamente. Como notificable() lo usan NotificarLogs, NotificarDigestNormal y NotificarUrgenteDigest, un cambio resuelve los 3 comandos.
  • app/Console/Commands/NotificarLogs.php: filtro ?->dispositivo !== null al inicio de incluirVecinosDeEnlace como defensa-en-profundidad para el caso "borrado entre query y procesamiento".
  • tests/Feature/NotificarLogsTest.php: test de regresión que crea estatus huérfano + estatus sano, soft-deletea el dispositivo del primero, corre el cron, valida exit code 0 + 1 solo correo (el sano).

Verificación:

  • Suite NotificarLogs 51/51 verde (era 50/50 + nuevo); suites NotificarDigestNormal + NotificarMonitoreosIgnorados + NotificarSitiosOffline 29/29 verde.
  • Suite global: 263 pasan, 4 falladas pre-existentes (todas del hotfix #193 — verificado con git stash).
  • Smoke prod post-deploy: ssh monitoreo "php artisan es:notificar-logs" → "No hay notificaciones pendientes" (limpio).

Out of scope: las 21 filas huérfanas siguen en BD pero ya no se notifican. Si Sergio quiere cleanup arquitectónico (cascade soft-delete DispositivoMonitoreoDispositivoEstatusMonitoreoDispositivo), capturo pendiente — no se hizo hoy.

Hub actualizado: projects/es-antenas-new.md (bitácora 2026-05-26 tarde + #191 marcado cerrado), PENDIENTES.md (#191 removido, contador -1, header actualizado), SPRINT.md (#191 marcado [x] en comprometido + entrada en "Cerrado este sprint").