Files
fn_registry/functions/infra/audit_projects_coverage.md
T
egutierrez eb8dbf66a1 feat(infra): auto-commit con 88 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-11 00:16:46 +02:00

7.1 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path, params, output
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports tested tests test_file_path file_path params output
audit_projects_coverage function go infra 1.0.0 impure func AuditProjectsCoverage(registryRoot string) ([]ProjectCoverage, error) Audita la cobertura de los projects del registry frente a sus sub-repos Gitea: comprueba si cada project tiene .git local, remote origin y repo_url declarado, y cuantos de sus hijos (apps + analyses) estan clonados en disco versus solo conocidos por la BD. Motor del subcomando fn doctor projects. Solo lee registry.db + filesystem + git local, nunca la red ni la API de Gitea. Incluye FindOrphanProjectRefs, el check inverso: detecta apps o analyses que declaran un project_id sin fila en la tabla projects (project paraguas huerfano, riesgo de perdida al sincronizar).
projects
gitea
subrepo
audit
infra
fn-doctor
doctor
false error_go_core
database/sql
os/exec
path/filepath
strings
text/tabwriter
github.com/mattn/go-sqlite3
true
healthy project con un hijo sin clonar marca children_missing
project sin repo_url ni remote marca no_gitea_repo
project sin directorio en disco marca dir_not_found
error si registry.db no existe
repo con origin devuelve true
repo sin origin devuelve false
sin issues lo deja claro
con issues cuenta los afectados
app con project_id huerfano lo detecta y agrupa ordenado
app con project_id valido no aparece
sin huerfanos devuelve slice vacio sin error
sin huerfanos lo deja claro
con huerfanos lista ids y cuenta
functions/infra/audit_projects_coverage_test.go functions/infra/audit_projects_coverage.go
name desc
registryRoot Raiz del repositorio (el directorio que contiene registry.db). Los dir_path relativos de projects, apps y analysis se resuelven contra esta raiz.
Slice de ProjectCoverage, una entrada por fila de la tabla projects, con flags de git/remote/repo_url, conteos de hijos clonados vs declarados, y la lista de issues detectados. La funcion de formato FormatProjectsCoverage produce una tabla de texto humano.

Ejemplo

package main

import (
	"fmt"

	"fn-registry/functions/infra"
)

func main() {
	rows, err := infra.AuditProjectsCoverage("/home/enmanuel/fn_registry")
	if err != nil {
		panic(err)
	}
	fmt.Print(infra.FormatProjectsCoverage(rows))
}

Salida típica:

PROJECT          GIT  REMOTE  REPO_URL  CHILDREN  ISSUES
fleet_monitoring  ✓    ✓       ✓         2/2       -
fn_monitoring     ✓    ✓       ✓         3/3       -
message_bus       ✓    ✓       ✓         3/4       children_missing
web_scraping      ✗    ✗       ✗         0/3       no_gitea_repo; children_missing

1/4 projects con problemas de cobertura.

Check inverso: FindOrphanProjectRefs

Mientras AuditProjectsCoverage parte de la tabla projects y mira hacia abajo (¿están sus hijos clonados?), FindOrphanProjectRefs recorre el grafo en sentido contrario: parte de las apps y analyses y mira hacia arriba (¿existe el project paraguas que declaran?). Detecta el drift inverso, apps o analyses cuyo project_id no tiene ninguna fila en la tabla projects. Es un project huérfano: existe en otro PC y nunca se sincronizó a este, o nunca se creó aquí. Es un riesgo de pérdida silenciosa, porque el enlace del hijo apunta a un project que este registro no conoce.

package main

import (
	"fmt"

	"fn-registry/functions/infra"
)

func main() {
	orphans, err := infra.FindOrphanProjectRefs("/home/enmanuel/fn_registry")
	if err != nil {
		panic(err)
	}
	fmt.Print(infra.FormatOrphanProjectRefs(orphans))
}

La firma es func FindOrphanProjectRefs(registryRoot string) ([]OrphanProjectRef, error). Cada OrphanProjectRef agrupa, por ProjectID huérfano, los ids de las apps (Apps) y analyses (Analyses) que lo referencian, ambas listas ordenadas alfabéticamente y el slice resultante ordenado por ProjectID. Cuando todos los hijos apuntan a un project conocido devuelve un slice vacío (no nil) sin error. FormatOrphanProjectRefs produce una tabla de texto humano con el project_id, cuántas apps y analyses lo referencian y sus ids; si no hay huérfanos imprime una sola línea dejándolo claro.

Caso real detectado en este registro: apps con project_id ∈ {element_agents, imagegen, osint_graph} sin fila correspondiente en projects.

Salida típica con huérfanos:

PROJECT_ID      APPS  ANALYSES  REFERENCED_BY
element_agents  1     0         shell_agent
imagegen        1     0         imagegen_ui
osint_graph     2     1         graph_explorer, scraper, gliner_glirel_tuning

3 project_id huérfanos (referenciados por hijos pero sin fila en projects).

Cuando usarla

Úsala antes de un /full-git-pull masivo o tras clonar el registry en un PC nuevo para saber qué projects están realmente respaldados por su sub-repo Gitea y cuántos de sus hijos (apps y analyses) quedarían sin clonar. También como motor del futuro subcomando fn doctor projects: el caller la enchufa desde cmd/fn/doctor.go igual que AuditUsesFunctions o AuditServicesSpec, formatea con FormatProjectsCoverage para texto humano y serializa el slice directamente para --json.

Gotchas

  • Es impura: lee registry.db (abierto en modo read-only ?mode=ro), recorre el filesystem y ejecuta git -C <dir> remote get-url origin. No toca la red ni la API de Gitea, así que no necesita token y es rápida.
  • HasRemote solo se evalúa cuando el project tiene .git local; si no hay .git, queda false sin intentar el comando git.
  • gitHasRemoteOrigin devuelve false ante cualquier error (no hay remote origin, no es un repo, git no instalado). No distingue "sin origin" de "git ausente"; si necesitas esa distinción, comprueba git por separado.
  • El issue no_gitea_repo se emite solo cuando faltan ambos indicadores (!HasRemote && !RepoURLDeclared). Un project con repo_url declarado pero sin clonar (dir_not_found) NO se marca no_gitea_repo — el repo existe en Gitea, simplemente no está en este disco.
  • ChildrenMissing cuenta los hijos (apps + analyses con ese project_id) cuya carpeta no tiene .git en disco: son los que se perderían o habría que reclonar. Cero hijos en la BD produce 0/0 y no genera issue.
  • Si projects.dir_path está vacío, se deriva projects/<id>. Los dir_path ya absolutos se respetan tal cual.
  • Devuelve error únicamente si registry.db no puede abrirse o consultarse. Los projects cuyo directorio no existe SÍ aparecen en el resultado, marcados con dir_not_found, para que el caller los muestre en vez de descartarlos en silencio.
  • FindOrphanProjectRefs también es impura: lee registry.db en modo read-only (?mode=ro), pero no toca el filesystem ni git, solo cruza las tablas projects, apps y analysis. Ignora los hijos con project_id vacío (no son huérfanos, simplemente no pertenecen a ningún project). Devuelve un slice vacío no-nil cuando no hay huérfanos, así que el caller puede distinguir "sin huérfanos" (slice vacío, error nil) de "fallo al leer la BD" (error no nil).