build_stdin_json enviaba ops_db_path tal cual al subprocess Python
(tipicamente "projects/<slug>/operations.db", relativo). Si el cwd
del proceso padre no era el dir del proyecto, sqlite3.connect
creaba un fichero vacio en otra ruta y el primer SELECT fallaba con
"no such table: entities".
Anade lambda absify que normaliza separadores (\\ -> /) antes de
std::filesystem::absolute (en Linux \\ es char literal del nombre,
no separador) y absolutiza ops_db, app_dir y registry_root antes
del to_wsl_path. Cubre los 5 enrichers de una sola vez.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sustituye el stub Windows por la implementacion real:
C++:
- Bloque #ifdef _WIN32 con CreateProcessW + 3 anonymous pipes
(CreatePipe + SetHandleInformation), STARTF_USESTDHANDLES,
CREATE_NO_WINDOW, ReadFile/WriteFile, WaitForSingleObject con polling
para soportar cancelacion via TerminateProcess.
- Helper to_wsl_path: convierte paths Windows a WSL antes de mandarlos
al subprocess. Soporta:
* "C:\\..." -> "/mnt/c/..."
* "\\\\wsl.localhost\\<distro>\\..." -> "/..."
* "\\\\wsl$\\<distro>\\..." -> "/..."
* "/..." -> tal cual
En POSIX la funcion es no-op.
- build_stdin_json siempre normaliza ops_db_path/app_dir/cache_dir/
registry_root a paths WSL — el run.py corre dentro de WSL y solo
entiende paths /home, /mnt, etc.
- Subprocess invocacion: `wsl.exe --cd <root_wsl> -- <python_wsl> <run_wsl>`.
Asume que el usuario tiene WSL instalado y la distro Ubuntu (o ajusta
FN_REGISTRY_ROOT al UNC adecuado).
- kill_proc unificado: TerminateProcess en Win32, kill(SIGTERM) en POSIX.
- JobControl con HANDLE+pid en Win32, pid_t en POSIX.
main.cpp:
- resolve_registry_root con fallback Windows: si FN_REGISTRY_ROOT env
no esta y getcwd no encuentra registry.db (caso del .exe en Desktop),
usa "\\\\wsl.localhost\\Ubuntu\\home\\lucas\\fn_registry". El usuario
cambia el UNC via env var si su distro tiene otro nombre.
Build:
- cpp/build/windows/apps/graph_explorer/graph_explorer.exe linkea limpio
contra MinGW; solo dependencias windows.h estandar (kernel32, etc.).
- Linux smoke test sigue detectando los 4 enrichers tras la
refactorizacion compartida.
Notas operativas para el usuario Windows:
- Ejecutar el .exe desde C:\\Users\\lucas\\Desktop\\apps\\graph_explorer\\
(doble clic). El primer arranque tarda ~1 s mas por cold-start de wsl.exe.
- Si la distro no es Ubuntu, setear FN_REGISTRY_ROOT con el UNC correcto
(ej. "\\\\wsl.localhost\\Debian\\home\\lucas\\fn_registry").
- Cancelar un job en Windows usa TerminateProcess (mas brutal que SIGTERM
pero los run.py no tienen estado critico — sqlite3 rollback automatico
por la transaccion implicita).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
El sistema de jobs usa fork+exec+pipes POSIX que no existen en MinGW.
Anade un stub _WIN32 que devuelve false en jobs_init y no-op en el resto,
de forma que la app compila para Windows pero los enrichers quedan
desactivados ahi. La build Linux/WSL conserva la implementacion completa.
TODO futuro: implementacion Windows con CreateProcess + anonymous pipes
+ TerminateProcess. No urgente — el desarrollo principal es WSL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Infra para correr enrichers en background mientras la app sigue interactiva.
C++:
- jobs.{h,cpp}: tabla jobs en graph_explorer.db, JobRunner con N=2 std::thread
workers, fork+exec POSIX con pipes, parser de PROGRESS:<float> <stage> en
stderr, captura de stdout JSON, persistencia + dirty_counter.
- enrichers.{h,cpp}: scanner de enrichers/<id>/manifest.yaml, parser YAML
minimo (id/name/description/applies_to), filtro por tipo de nodo.
- views_jobs.cpp: panel "Jobs" dockeable con tabla (status/enricher/target/
progress/time), filtro all/active/done/errors, cancelar/borrar inline.
Wiring:
- main.cpp: resolve_registry_root() (FN_REGISTRY_ROOT env o subir desde cwd
buscando registry.db), jobs_init/enrichers_load antes de fn::run_app,
jobs_shutdown al cerrar, dirty_counter -> want_reload, jobs_set_ops_db al
cambiar de proyecto.
- main.cpp:render_context_menu: menu "Run enricher" sustituye placeholder
con submenu filtrado por type_ref via enrichers_for_type. Submit abre
panel Jobs auto.
- views.h: AppState::panel_jobs flag + decl views_jobs().
- CMakeLists.txt: anade jobs.cpp + enrichers.cpp + views_jobs.cpp y enlaza
Threads::Threads.
Wire protocol enricher (subprocess Python):
- stdin: JSON con node_id, metadata, ops_db_path, app_dir, cache_dir,
registry_root, params.
- stderr: PROGRESS:<float> <stage> + LOG lineas libres.
- stdout: JSON resumen al final.
- exit 0 = ok, !=0 = error con stderr capturado en panel Jobs.
El run.py escribe directamente al operations.db (sqlite3 stdlib) — C++ solo
orquesta, no parsea entities/relations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>