Files
fn_registry/dev/issues/0171-project-subrepo-manifest-and-reclone.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

200 lines
10 KiB
Markdown

---
id: "0171"
title: "Manifest de sub-repos por project + re-clonado y auditoría de cobertura en Gitea"
status: pendiente
type: enhancement
domain:
- registry-quality
- infra
scope: registry-only
priority: alta
depends: []
blocks: []
related: ["0166"]
created: 2026-06-10
updated: 2026-06-10
tags: [projects, subrepo, gitea, clone, backup, manifest, fn-doctor]
---
> **Actualización 10/06/2026 — implementado el núcleo (enfoque KISS).** El manifest
> `subrepos.yaml` propuesto abajo se **descartó**: `registry.db` (tablas `apps`/`analysis`
> con `project_id`, propagadas entre PCs por `fn sync`) **ya es** el manifest de sub-repos, y
> `clone_project_subrepos_bash_pipelines` ya lo consume. No hace falta un archivo nuevo. Lo que
> faltaba era integración + auditoría. Ver `## Estado de implementación` al final.
# 0171 — Manifest de sub-repos por project + re-clonado y auditoría de cobertura en Gitea
## APP Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0171 |
| **Estado** | pendiente |
| **Prioridad** | alta (riesgo de pérdida de datos) |
| **Tipo** | enhancement — metadata de projects + `/full-git-pull` + `fn doctor` |
## Contexto
El 10/06/2026, al preparar un dashboard sobre el project `aurgi`, se descubrió que el project
paraguas **no existía en Gitea** (`dataforge/aurgi` → 404). Sus 3 analyses sí estaban a salvo como
sub-repos independientes (`dataforge/venta_web`, `dataforge/sale_prices_comprobation`,
`dataforge/presupuestos_callcenter`), pero **el `project.md`, `vault.yaml` y `CONVENTIONS.md` de
nivel-project no estaban versionados en ningún sitio**. Reconstruir el project obligó a *adivinar*
los nombres de los sub-repos hijos uno a uno desde la lista completa de repos de Gitea.
Una auditoría de cobertura `projects ↔ Gitea` confirmó el agujero:
| Project | Repo Gitea | Riesgo |
|---|---|---|
| fleet_monitoring, fn_monitoring, message_bus, web_scraping | ✅ | ninguno |
| **obsidian**, **osint** | ❌ (solo en disco local) | alto — resuelto en esta sesión (subidos a `dataforge/obsidian`, `dataforge/osint`) |
| **aurgi** | ❌ (404, paraguas inexistente) | pendiente — analyses salvados, docs nivel-project no |
Dos problemas estructurales quedan abiertos:
1. **Projects sin repo Gitea**: su contenido de nivel-project vive solo en disco. Si se borra el
disco (o el project no se sincroniza a otro PC), se pierde. La regla `projects.md` dice que cada
project debe ser su propio repo Gitea, pero no hay nada que lo **verifique ni lo fuerce**.
2. **Sub-repos hijos no referenciados**: el `.gitignore` de cada project excluye `apps/*/` y
`analysis/*/` (son sub-repos independientes). Por tanto, **un clon fresco del project NO trae sus
hijos**, y no existe ningún manifest que diga *qué hijos clonar*. Hoy `/full-git-pull` solo
descubre repos vía `discover_git_repos_bash_infra` (busca `.git` ya presentes en disco): si el
hijo nunca se clonó, es invisible. Resultado: para reconstruir un project en una máquina nueva hay
que adivinar sus sub-repos (exactamente lo que pasó con aurgi).
## Objetivo
Que **todo project** (a) tenga su repo Gitea garantizado y (b) **referencie declarativamente sus
sub-repos hijos** (apps + analyses), de modo que clonar el project en cualquier PC permita
re-clonar automáticamente todo su árbol sin adivinar nada.
## Propuesta
### 1. Manifest de sub-repos por project
Añadir a cada project un manifest declarativo de sus hijos. Dos opciones de formato (decidir una):
- **Opción A (KISS, preferida): `subrepos.yaml`** en la raíz del project, análogo a `vault.yaml`:
```yaml
# projects/<p>/subrepos.yaml — sub-repos hijos de este project (apps + analyses)
subrepos:
- kind: analysis # app | analysis
name: venta_web
path: analysis/venta_web
repo: dataforge/venta_web
url: https://gitea-.../dataforge/venta_web
- kind: analysis
name: sale_prices_comprobation
path: analysis/sale_prices_comprobation
repo: dataforge/sale_prices_comprobation
url: https://gitea-.../dataforge/sale_prices_comprobation
```
- **Opción B: sección `## Sub-repos`** en `project.md` con una tabla `kind | name | path | url`.
`subrepos.yaml` (Opción A) es más fácil de parsear por las funciones de git y se versiona con el
project (no está en el `.gitignore`). El manifest se **autogenera/actualiza** escaneando los `.git`
hijos presentes en disco + su `remote get-url origin` (reusar `discover_git_repos_bash_infra`).
### 2. Generación y mantenimiento del manifest
Función/pipeline nueva (delegar a `fn-constructor`, grupo `infra`/git) que, dado un project:
- Escanea `apps/*/.git` y `analysis/*/.git`, lee su remote origin.
- Escribe/actualiza `subrepos.yaml`.
- Idempotente. Se invoca dentro de `/full-git-push` (o `fn index`) para mantener el manifest al día.
### 3. Re-clonado desde el manifest en `/full-git-pull`
Extender `/full-git-pull` para que, tras actualizar cada project, lea su `subrepos.yaml` y **clone
los hijos que falten** (`url` → `path`). Así, en un PC nuevo: clonar `dataforge/<project>` →
`/full-git-pull` → reconstruye apps + analyses automáticamente. Requiere una función
`clone_missing_subrepos_bash_infra(project_dir)` (delegar a `fn-constructor`).
### 4. Garantizar repo Gitea de cada project + auditoría en `fn doctor`
- Subcomando nuevo `fn doctor projects` (función `audit_projects_coverage_go_infra`): por cada
project en disco reporta `repo_gitea` (existe en Gitea sí/no), `repo_url` (declarado en project.md
sí/no), y `subrepos_manifest` (presente + cuántos hijos en disco sin entrada / en manifest sin
clonar). Salida `--json`. Cero hallazgos = sano.
- Acción derivada documentada: `repo_gitea=no` → `ensure_repo_synced_bash_infra projects/<p>
dataforge <p> master "init: project <p>"`.
### 5. Backfill inicial
- `aurgi`: traer su `project.md` / `vault.yaml` / `CONVENTIONS.md` de `aurgi-pc` (o `home-wsl`) y
crear `dataforge/aurgi` + `subrepos.yaml` con los 3 analyses ya conocidos. **No** reconstruir a
mano un `project.md` mínimo (divergiría del real).
- Resto de projects con hijos (`fleet_monitoring`, `fn_monitoring`, `message_bus`, `web_scraping`):
generar su `subrepos.yaml` con la función del punto 2.
## Definition of Done
| Escenario | Tipo | Comando / evidencia | Resultado esperado |
|---|---|---|---|
| Golden: clon fresco reconstruye árbol | e2e | clonar `dataforge/<p>` en dir limpio → `/full-git-pull` | apps + analyses del project re-clonados desde `subrepos.yaml` |
| Edge: project sin hijos (obsidian) | e2e | generar manifest | `subrepos.yaml` válido y vacío (o ausente), sin error |
| Edge: hijo en disco sin `.git` | unit | auditoría | `fn doctor projects` lo reporta como "hijo sin sub-repo" |
| Error: project sin repo Gitea | e2e | `fn doctor projects --json` | lo marca `repo_gitea=false`, sugiere `ensure_repo_synced` |
| Cobertura | audit | `fn doctor projects` | 0 projects sin repo, 0 hijos sin referenciar |
## Decisiones abiertas
1. **Formato del manifest**: `subrepos.yaml` (A) vs. sección en `project.md` (B). Recomendado A.
2. **¿Auto-generar el manifest en `fn index`** o solo en `/full-git-push`? (evitar I/O de red en
`fn index`; preferible en push).
3. **aurgi**: ¿traer de `aurgi-pc` por SSH ahora, o dejarlo para cuando el project se sincronice?
## Notas
En esta sesión ya se resolvió el riesgo inmediato: `obsidian` y `osint` se subieron a Gitea
(`dataforge/obsidian`, `dataforge/osint`) con `ensure_repo_synced_bash_infra` y se les añadió
`repo_url` en su `project.md`. Este issue cubre la solución **estructural y reutilizable** para que
el caso no vuelva a ocurrir con ningún project. Relacionado con #0166 (dependencias app→app para
build reproducible): ambos persiguen que clonar el ecosistema en un PC nuevo sea determinista.
## Estado de implementación (10/06/2026)
Implementado con enfoque KISS, **sin** `subrepos.yaml` (registry.db + `fn sync` ya cumplen esa
función). Cambios:
**Funciones nuevas:**
- `ensure_project_gitignore_bash_infra` — garantiza idempotente el `.gitignore` canónico de un
project (`apps/*/`, `analysis/*/`, `vaults/*` + excepciones) antes de cualquier `git add -A`,
para no trackear el contenido de los sub-repos hijos.
- `audit_projects_coverage_go_infra` (+ `FormatProjectsCoverage`) — motor de `fn doctor projects`.
Reporta por project: `git`/`remote`/`repo_url`/`children (cloned/inDB)` + issues
(`no_gitea_repo`, `children_missing`, `dir_not_found`). Solo git local + registry.db, sin red.
**Integraciones:**
- `full_git_push` v1.1.0 — paso 1c: auto-inicializa y pushea los **projects paraguas** sin repo
(antes solo apps/analyses), asegurando el `.gitignore` canónico primero. Cierra el agujero
aurgi/obsidian/osint.
- `full_git_pull` v1.1.0 — paso 6: tras `fn sync`, reclona los sub-repos hijos faltantes de cada
project con `clone_project_subrepos` + re-index. Clonar el paraguas + `/full-git-pull` reconstruye
el árbol entero.
- `fn doctor projects` — nuevo subcomando (`cmd/fn/doctor.go`). Hoy reporta **0 projects con
problemas**.
**Hecho aparte (riesgo inmediato):** `dataforge/obsidian` + `dataforge/osint` creados, `repo_url`
en sus `project.md`.
### Pendientes (no bloquean el núcleo)
1. **Check inverso — HECHO (10/06/2026).** `FindOrphanProjectRefs` + `FormatOrphanProjectRefs` en
`audit_projects_coverage_go_infra`, enchufado en `fn doctor projects`. Detecta apps/analysis con
`project_id` sin fila en `projects`. Hoy reporta 4 paraguas huérfanos (existen en otro PC, nunca
subidos a Gitea — mismo caso que aurgi):
- `element_agents` (6 apps: agents_and_robots, agents_dashboard, device_agent, element_matrix_chat,
matrix_admin_panel, matrix_client_pc)
- `imagegen` (image_to_3d_studio)
- `osint_graph` (graph_explorer)
- `aurgi` (sus analyses sí están en Gitea; el paraguas no)
2. **Fix de datos de los 4 paraguas huérfanos — pendiente, requiere el PC origen.** No están en disco
ni en Gitea en este PC (`lucas-linux`), así que no se pueden reconstruir aquí sin inventar. El fix
correcto: correr `/full-git-push` en el PC donde cada paraguas existe en disco (`aurgi-pc` /
`home-wsl`). Con `full_git_push` v1.1.0 (paso 1c) eso ya los crea en Gitea automáticamente. Tras
eso, `/full-git-pull` aquí (paso 6) los traerá. NO reconstruir un `project.md` mínimo a mano.
3. **DoD vida útil**: validar el reclonado en un PC nuevo real (clon limpio del paraguas →
`/full-git-pull` → árbol reconstruido) antes de declarar el issue cerrado.