--- name: audit_capability_groups kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func AuditCapabilityGroups(root string) ([]CapabilityGroupAudit, error)" description: "Audita drift entre grupos declarados en docs/capabilities/INDEX.md, tags presentes en registry.db y paginas existentes en docs/capabilities/. Detecta grupos sin doc, sin funciones, candidatos sin declarar y docs huerfanas." tags: [doctor, capability-groups, audit, infra] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: - "database/sql" - "bufio" - "fmt" - "os" - "path/filepath" - "regexp" - "sort" - "strings" - "github.com/mattn/go-sqlite3" tested: false tests: [] test_file_path: "" file_path: "functions/infra/audit_capability_groups.go" params: - name: root desc: "raiz del repo fn_registry (donde vive registry.db y docs/capabilities/)" output: "lista de CapabilityGroupAudit ordenada por group asc, con drift detectado" --- ## Tipo: CapabilityGroupAudit ```go type CapabilityGroupAudit struct { Group string `json:"group"` // slug del grupo (tag canonico) DeclaredInIndex bool `json:"declared_in_index"` // aparece en INDEX.md DocExists bool `json:"doc_exists"` // existe docs/capabilities/.md FunctionCount int `json:"function_count"` // funciones con ese tag Issues []string `json:"issues"` // lista de problemas detectados OK bool `json:"ok"` // true si Issues esta vacio } ``` ## Issues posibles | Issue | Condicion | |---|---| | `doc_missing` | Grupo declarado en INDEX pero no existe el .md | | `no_functions` | Grupo declarado pero ningun tag en registry.db coincide | | `below_minimum` | Grupo declarado con 1 o 2 funciones (minimo es 3) | | `ungrouped_candidate` | Tag con >= 3 funciones, no declarado en INDEX ni tiene .md | | `doc_orphan` | Existe docs/capabilities/.md pero no esta en INDEX.md | ## Ejemplo ```go audits, err := AuditCapabilityGroups("/home/lucas/fn_registry") if err != nil { log.Fatal(err) } for _, a := range audits { if !a.OK { fmt.Printf("%s: %v\n", a.Group, a.Issues) } } ``` ## Notas - Abre `registry.db` en modo read-only (no modifica nada). - Parsea INDEX.md con regex `\[([a-z][a-z0-9_-]*)\]\(\1\.md\)` — solo links donde el texto y el href tienen el mismo slug. - Tags con count >= 3 que no estan en INDEX se emiten como `ungrouped_candidate`. - Si INDEX.md no existe, se trata como conjunto vacio (no es error). - Usada por `fn doctor capabilities` para diagnostico del registry.