---
name: vault_aggregate_index
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func VaultAggregateIndex(repoRoot string) (AggregateReport, error)"
description: "Agrega los índices de todos los vaults del registry en la tabla vault_files de registry.db. Lee cada vault_index.db (via VaultIndexOpen) y reemplaza las filas de forma atómica. Idempotente: re-ejecutar limpia y reescribe sin duplicar."
tags: [vault, index, aggregate, registry]
uses_functions:
  - "vault_manifest_read_go_infra"
  - "vault_index_open_go_infra"
  - "sqlite_open_go_infra"
uses_types:
  - "vault_file_go_infra"
returns: []
returns_optional: false
error_type: "error_go_core"
imports:
  - "database/sql"
  - "fmt"
  - "os"
  - "path/filepath"
  - "time"
tested: true
tests:
  - "TestVaultAggregateIndex_NoVaults"
  - "TestVaultAggregateIndex_VaultWithoutIndex"
  - "TestVaultAggregateIndex_HappyPath"
  - "TestVaultAggregateIndex_ReRunReplaces"
test_file_path: "functions/infra/vault_aggregate_index_test.go"
file_path: "functions/infra/vault_aggregate_index.go"
params:
  - name: repoRoot
    desc: "Ruta absoluta a la raiz del fn_registry (contiene registry.db y projects/)."
output: "AggregateReport con VaultsProcessed, VaultsSkipped (sin vault_index.db), TotalFiles y Errors (errores no fatales por vault). Error fatal solo si registry.db no se puede abrir."
---

## Ejemplo

```go
report, err := infra.VaultAggregateIndex("$HOME/fn_registry")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Processed: %d vaults, %d files\n", report.VaultsProcessed, report.TotalFiles)
for _, e := range report.Errors {
    fmt.Println("warning:", e)
}
```

## Notas

- Requiere que `registry/migrations/012_vault_files.sql` haya sido aplicado (o que el indexer lo aplique al arrancar). La función aplica la migración de forma idempotente ella misma con `CREATE TABLE IF NOT EXISTS`.
- Por cada vault: `DELETE WHERE vault_id = ?` + batch `INSERT` dentro de una transacción. Re-run siempre produce el mismo resultado.
- Vaults sin `vault_index.db` se cuentan en `VaultsSkipped` y se omiten sin error.
- El `vault_id` sigue el patrón `<vault_name>_<project_id>`, consistente con la tabla `vaults` de registry.db.
