--- name: vault_doctor kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func VaultDoctor(repoRoot string) ([]VaultDoctorEntry, error)" description: "Audita la salud de todos los vaults declarados en projects/*/vaults/vault.yaml. Comprueba existencia del directorio, layout estándar, presencia del índice, staleness y drift entre disco e índice. Read-only." tags: [vault, doctor, health, audit] uses_functions: - "vault_manifest_read_go_infra" - "vault_index_open_go_infra" uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: - "fmt" - "os" - "path/filepath" - "strings" - "time" tested: true tests: - "TestVaultDoctor_OK" - "TestVaultDoctor_MissingDir" - "TestVaultDoctor_NoIndex" - "TestVaultDoctor_LayoutDrift" - "TestVaultDoctor_EmptyVault" test_file_path: "functions/infra/vault_doctor_test.go" file_path: "functions/infra/vault_doctor.go" params: - name: repoRoot desc: "Ruta absoluta a la raiz del fn_registry (donde están projects/ y registry.db)." output: "Slice de VaultDoctorEntry con Status (ok/warning/error), Issues, DiskFiles, IndexedFiles y LastIndexedAt por vault. Error fatal solo si los manifests no se pueden leer." --- ## Checks aplicados | Check | Condición | Severidad | |---|---|---| | `directory_missing` | `e.Path` no existe en disco | error | | `layout_missing` | no hay `data/` ni `knowledge/` en la raíz del vault | warning | | `non_standard_layout` | no hay `data/`/`knowledge/` pero sí otros subdirectorios (ej. imagegen_models) | warning | | `index_missing` | no existe `vault_index.db` | warning | | `index_stale` | algún archivo en disco tiene mtime > MAX(indexed_at) | warning | | `index_drift` | count disco != count en tabla `files` | warning | | `empty_vault` | DiskFiles == 0 | warning | ## Ejemplo ```go entries, err := infra.VaultDoctor("$HOME/fn_registry") for _, e := range entries { fmt.Printf("%-30s %-8s files=%d issues=%v\n", e.VaultName, e.Status, e.DiskFiles, e.Issues) } ``` ## Notas - Función read-only: nunca escribe en disco ni en ninguna base de datos. - `countDiskFiles` usa `filepath.WalkDir` sin hash (cheap) — excluye `vault_index.db*`, `.git/` y ficheros ocultos. - `isIndexStale` también usa WalkDir; compara mtime de archivos con MAX(indexed_at) de la BD. - El VaultIndexOpen de sólo lectura no crea el DB (si no existe, retorna error y se reporta `index_missing`).