--- name: audit_app_drift kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func AuditAppDrift(registryRoot string, windowsDeployRoot string) ([]AppDriftEntry, error)" description: "Escanea todas las apps C++ del registry, extrae las versiones de modulos embebidas en sus artefactos compilados (exe Windows desplegado, modules_generated.cpp Windows/Linux) y las compara contra la version declarada en modules/*/module.md. Retorna una entrada por app con status ok|drift|no-build y la lista de modulos desactualizados." tags: [audit, drift, cpp, modules, doctor, registry] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: error_go_core imports: - fmt - os - path/filepath - regexp - sort - strings tested: false tests: [] test_file_path: "" file_path: "functions/infra/audit_app_drift.go" params: - name: registryRoot desc: "Ruta absoluta a la raiz del fn_registry (ej. $HOME/fn_registry). Se buscan modules/*/module.md, apps/*/app.md, projects/*/apps/*/app.md y cpp/build/{linux,windows}/apps//." - name: windowsDeployRoot desc: "Ruta absoluta donde se despliegan los .exe de Windows (ej. /mnt/c/Users/lucas/Desktop/apps). Si vacio, se omite la inspeccion del binario desplegado y solo se usan los artefactos bajo cpp/build." output: "Slice de AppDriftEntry, uno por app C++ encontrada. Status: 'ok' (todas las versiones coinciden), 'drift' (al menos un modulo tiene version distinta), 'no-build' (no se encontro ningun artefacto compilado). Campo Stale lista los nombres de modulos con drift." --- # audit_app_drift Detecta apps C++ cuyos binarios/artefactos enlazan una version de modulo inferior a la que declara el registry. ## Cuando usarla - Despues de bumpar la version de un modulo en `modules/*/module.md` — para saber que apps necesitan recompilarse. - Como check en `fn doctor cpp-apps` para detectar drift antes de un deploy. - En CI tras actualizar `fn_framework` o `fn_module_data_table` — lista apps con `status=drift`. ## Ejemplo ```go entries, err := infra.AuditAppDrift("$HOME/fn_registry", "/mnt/c/Users/lucas/Desktop/apps") if err != nil { log.Fatal(err) } for _, e := range entries { switch e.Status { case "drift": fmt.Printf("STALE %s: modules %v (build: %s)\n", e.AppName, e.Stale, e.BuildKind) case "no-build": fmt.Printf("NOBLD %s: no compiled artifact found\n", e.AppName) case "ok": fmt.Printf("OK %s (%s)\n", e.AppName, e.BuildKind) } } ``` Salida tipica: ``` STALE app_gestion: modules [data_table] (build: linux-build) OK chart_demo (windows-deployed) NOBLD primitives_gallery: no compiled artifact found ``` ## Prioridad de artefactos (por app) 1. `//.exe` — PE binario: busca patron `\x00\x00` directo en bytes. 2. `cpp/build/windows/apps//_modules_generated.cpp` — parsea literales `{ "module", "version", ... }`. 3. `cpp/build/linux/apps//_modules_generated.cpp` — mismo parser. Si ninguno existe → `status = "no-build"`, `BuildKind = ""`. ## Gotchas - **PE vs ELF string layout**: en ELF (Linux) el compilador puede separar las cadenas del struct en distintas regiones del `.rodata`, por lo que la busqueda directa en el binario no es fiable. Por eso se prefiere el `_modules_generated.cpp` (generado por `codegen_app_modules.py` al compilar). El binario PE Windows si tiene el patron contiguo. - **Sin registry.db**: la funcion lee `module.md` directamente — no necesita la BD. Si el `fn index` no se ha ejecutado tras un bump de version, esta funcion igual detecta el drift porque lee la fuente de verdad (el `.md`). - **Apps sin `uses_modules`**: si una app C++ no declara `uses_modules` ni tiene `_modules_generated.cpp`, aparece con `status=no-build` aunque compile correctamente. Normal para apps que no usan modulos opcionales. - **Solo compara modulos del registry**: si el binario enlaza un modulo desconocido (que no tiene `module.md`), se ignora silenciosamente en el calculo de drift. - **windowsDeployRoot vacio**: si el usuario no tiene montado `/mnt/c/...` (no WSL2), pasar `""` para evitar `os.Stat` lento en rutas inexistentes.