El pool nunca guardaba el PID del Chrome lanzado por browser_launch, así que
closeAll() y drop() cerraban con CdpClose(c, 0): solo soltaban el WebSocket y
dejaban el proceso chromium vivo y huérfano (~789 MiB RSS cada uno). Llamadas
repetidas a browser_launch acumulaban instancias sin límite hasta saturar la RAM
(apagón del 06/06/2026, ~35 chromium huérfanos).
Cambios:
- pool.go: el pool registra el PID lanzado por puerto (mapa `pids`) con
setPID/getPID/clearPID/launchedCount. drop() y closeAll() matan el grupo de
proceso completo (CdpClose con pid real) SOLO si el PID está registrado, es
decir, si lo lanzó el MCP. Un Chrome externo sin PID registrado (el navegador
diario del usuario en 9222) nunca se mata: pid=0 solo cierra el WebSocket.
Nuevo releaseConn() suelta únicamente el WebSocket preservando el PID, para la
reconexión interna (no debe matar el navegador).
- tools_session.go: handleLaunch registra el PID devuelto por ChromeLaunch
(setPID); es idempotente por puerto (reusa el Chrome ya lanzado), pasa
ReuseExisting=true para no duplicar un Chrome ya vivo en el puerto, y aplica
un tope duro de 4 instancias (maxLaunchedChromes) devolviendo un error de tool
al superarlo. browser_disconnect ahora mata el Chrome propio.
- main.go: handler SIGTERM/SIGINT que llama closeAll antes de salir (los defers
no corren al recibir señal). El retry de withConn usa releaseConn en vez de
drop para no matar el Chrome al reconectar.
- pool_test.go: tests lógicos sin Chrome (cap, idempotencia, ciclo de PID, drop).
- pool_e2e_test.go: tests con Chrome real (gate BMCP_E2E=1) — golden (3 launch →
closeAll → 0 huérfanos), dedup mismo puerto, y salvaguarda propio-vs-externo.
- app.md: e2e_checks (build, unit, leak_no_orphans) + growth log + bump a 0.5.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CdpHandleDialog ahora devuelve (cancel, *DialogLog, error). El pool guarda el
DialogLog por puerto y browser_disconnect reporta cuántos diálogos se
auto-respondieron y el último (tipo + mensaje). drop/closeAll usan CdpDisconnect
(alias legible de CdpClose(c,0)).