feat(infra): auto-commit con 88 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
id: "0172"
|
||||
title: "App web OSINT: grafo sigma.js + tablas por tipo + fichas con imágenes sobre el vault osint"
|
||||
status: pendiente
|
||||
type: app
|
||||
domain:
|
||||
- osint
|
||||
- frontend
|
||||
scope: app-scoped
|
||||
priority: media
|
||||
depends: []
|
||||
blocks: []
|
||||
related: ["0171"]
|
||||
created: 2026-06-10
|
||||
updated: 2026-06-10
|
||||
tags: [osint, web, sigma, graph, mantine, obsidian, vault, dashboard]
|
||||
---
|
||||
# 0172 — App web OSINT: grafo sigma.js + tablas por tipo + fichas con imágenes
|
||||
|
||||
## APP Metadata
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **ID** | 0172 |
|
||||
| **Estado** | pendiente (solo plan — se construye cuando el vault tenga más datos) |
|
||||
| **Prioridad** | media |
|
||||
| **Tipo** | app — nueva app web en `projects/osint/apps/osint_web` |
|
||||
| **Project** | osint (`projects/osint/`) |
|
||||
|
||||
## Contexto
|
||||
|
||||
El project `osint` guarda sus investigaciones en el vault de Obsidian
|
||||
`/home/enmanuel/Obsidian/osint` (sub-repo `dataforge/osint`). Hoy ese vault tiene:
|
||||
|
||||
- **~82 nodos** repartidos en carpetas tipadas: `personas/` (45), `organizaciones/` (25),
|
||||
`lugares/` (10), `dominios/` (1), `casos/` (1).
|
||||
- **Datos tabulares** en el frontmatter YAML de cada ficha: `tipo`, `nombre`, `sexo`,
|
||||
`fecha_nacimiento`, `dni`, `direccion`, `pais`, `aliases`, `tags`, etc.
|
||||
- **Aristas implícitas**: los wikilinks `[[...]]` en las secciones `Relaciones`, `Lugares` y
|
||||
`Documentos` conectan unas fichas con otras (y con sus attachments).
|
||||
- **~240 attachments**: fotos, DNIs, certificados y PDFs en `attachments/<tipo>/<slug>/`,
|
||||
embebidos en las notas con `![[...]]`.
|
||||
|
||||
Obsidian es bueno para *escribir* la investigación, pero malo para *explorarla* de un vistazo:
|
||||
no da un grafo navegable de todos los objetivos, ni una tabla filtrable, ni una ficha-resumen
|
||||
con la galería de imágenes de cada persona. Metabase/Grafana no encajan: leen BD SQL (no `.md`),
|
||||
y no muestran ni grafo de nodos ni imágenes inline.
|
||||
|
||||
Decisión del usuario (10/06/2026): construir una **app web propia** que lea el vault y ofrezca
|
||||
tres vistas — **grafo explorable con sigma.js**, **tablas filtradas por tipo**, y **fichas con
|
||||
imágenes**. Este issue es **solo el plan**: la recopilación de datos en Obsidian continúa primero;
|
||||
la app se implementa cuando haya suficiente material que justifique la inversión.
|
||||
|
||||
## Objetivo
|
||||
|
||||
Una app web local que, leyendo directamente los `.md` del vault `osint` (sin BD intermedia
|
||||
obligatoria en v1), permita:
|
||||
|
||||
1. **Explorar el grafo** de nodos (personas, organizaciones, lugares, dominios, casos) y sus
|
||||
conexiones por wikilinks, con sigma.js: zoom, pan, click en nodo → ficha, colores por tipo,
|
||||
filtro de tipos visibles, búsqueda de nodo.
|
||||
2. **Ver tablas filtradas por tipo**: una tabla por categoría (personas, organizaciones, ...)
|
||||
con las columnas del frontmatter, ordenable y filtrable (por dni, lugar, fecha, tag).
|
||||
3. **Abrir la ficha** de cualquier nodo: frontmatter renderizado + cuerpo Markdown + galería de
|
||||
sus attachments (fotos, DNIs, PDFs) servidos por el backend.
|
||||
|
||||
## Arquitectura propuesta
|
||||
|
||||
```
|
||||
projects/osint/apps/osint_web/ (sub-repo Gitea dataforge/osint_web)
|
||||
app.md frontmatter de registro (framework: react-vite-mantine)
|
||||
server/ backend Python (lee el vault, sirve JSON + attachments)
|
||||
main.py FastAPI o stdlib http
|
||||
frontend/ React + Vite + Mantine + sigma.js
|
||||
src/
|
||||
views/GraphView.tsx sigma.js + graphology
|
||||
views/TablesView.tsx Mantine DataTable filtrable por tipo
|
||||
views/NodeCard.tsx ficha + galería de attachments
|
||||
```
|
||||
|
||||
### Backend (Python — máximo reuso del grupo `obsidian`)
|
||||
|
||||
Python porque el grupo de capacidad `obsidian` (11 funciones, dominio `obsidian`) ya cubre casi
|
||||
todo el parseo del vault. **Registry-first**: el backend orquesta estas funciones, no reimplementa
|
||||
el parseo.
|
||||
|
||||
Funciones del registry a reutilizar:
|
||||
|
||||
| Función | Uso en la app |
|
||||
|---|---|
|
||||
| `list_obsidian_notes_py_obsidian` | enumerar nodos por carpeta/tipo |
|
||||
| `read_obsidian_note_py_obsidian` | leer ficha: `{frontmatter, body, wikilinks, tags}` |
|
||||
| `parse_obsidian_frontmatter_py_obsidian` | datos tabulares de cada nodo |
|
||||
| `extract_obsidian_wikilinks_py_obsidian` | aristas del grafo |
|
||||
| `extract_obsidian_embeds_py_obsidian` | attachments embebidos en cada nota |
|
||||
| `resolve_obsidian_embed_py_obsidian` | resolver `![[foto.jpg]]` → path real en disco para servir la imagen |
|
||||
| `slugify_obsidian_name_py_obsidian` | normalizar nombre de wikilink → id de nodo |
|
||||
| `search_obsidian_notes_py_obsidian` | búsqueda global en el grafo |
|
||||
|
||||
Funciones **nuevas** a delegar a `fn-constructor` (no escribir inline en la app):
|
||||
|
||||
- `build_obsidian_graph_py_obsidian` (impure) — dado `vault_dir`, devuelve
|
||||
`{"nodes": [{id, tipo, label, frontmatter}], "edges": [{source, target, kind}]}`.
|
||||
Resuelve cada wikilink a un nodo existente (vía slug / nombre de archivo); los wikilinks que
|
||||
no resuelven a un `.md` del vault se marcan como aristas "dangling" o se descartan según flag.
|
||||
Tag de grupo: `obsidian`. Es la pieza que el grupo declara como frontera no cubierta
|
||||
("No indexa el grafo agregado") — esta función la cierra.
|
||||
|
||||
Endpoints HTTP (JSON salvo el de attachments):
|
||||
|
||||
| Método | Ruta | Devuelve |
|
||||
|---|---|---|
|
||||
| GET | `/api/graph` | grafo completo `{nodes, edges}` para sigma.js |
|
||||
| GET | `/api/nodes?tipo=persona` | filas de la tabla de ese tipo (frontmatter aplanado) |
|
||||
| GET | `/api/node/{slug}` | ficha: frontmatter + body (HTML/markdown) + lista de attachments |
|
||||
| GET | `/api/attachment?path=...` | sirve el binario del attachment (image/pdf), con allowlist al vault |
|
||||
| GET | `/api/search?q=...` | nodos que matchean |
|
||||
|
||||
Seguridad: el backend solo sirve archivos **dentro** del vault osint (path traversal bloqueado).
|
||||
El vault contiene datos personales sensibles (DNIs) → la app escucha **solo en `127.0.0.1`**, sin
|
||||
exponer a red. No es un service desplegable a VPS.
|
||||
|
||||
### Frontend (React + Vite + Mantine + sigma.js)
|
||||
|
||||
- Sistema del registry: React + Vite + Mantine v9 + `@fn_library` (grupo `mantine`, 63 funciones).
|
||||
Componentes propios de `@fn_library` antes que HTML nativo (regla `frontend_theming.md`).
|
||||
- **Grafo**: `sigma.js` + `graphology`. Color por `tipo`, tamaño por grado, layout
|
||||
force-directed (graphology-layout-forceatlas2). Click en nodo → abre `NodeCard`. Panel lateral
|
||||
con toggles de tipos visibles y caja de búsqueda.
|
||||
- **Tablas**: una pestaña por tipo, Mantine `Table`/DataTable con columnas del frontmatter,
|
||||
orden y filtro por columna (dni, lugar, fecha_nacimiento, tags).
|
||||
- **Fichas**: `NodeCard` con frontmatter en formato clave-valor (fechas en formato europeo
|
||||
DD/MM/AAAA — memoria `formato-fecha-europeo`), cuerpo Markdown, y galería de attachments
|
||||
(imágenes con lightbox; PDFs como enlace/embed).
|
||||
|
||||
`sigma.js` y `graphology` son dependencias nuevas del frontend (no en `@fn_library`). KISS:
|
||||
añadir solo esas dos; el resto (tabla, layout, modales) sale de Mantine/`@fn_library`.
|
||||
|
||||
## Decisiones abiertas
|
||||
|
||||
1. **¿BD intermedia o lectura directa del vault?** v1 lee el vault en cada arranque (cachea el
|
||||
grafo en memoria). Si el vault crece mucho o se quiere histórico/diff, evaluar un
|
||||
`operations.db` con `entities`/`relations` (encaja con el bucle reactivo). Recomendado:
|
||||
empezar sin BD (KISS), añadirla solo si el rendimiento o un caso de uso lo exige.
|
||||
2. **Backend FastAPI vs stdlib http**: FastAPI da validación y OpenAPI gratis; stdlib evita una
|
||||
dependencia. Como el backend es fino (orquesta funciones del registry), decidir al construir.
|
||||
3. **Live-reload del vault**: ¿re-escanear bajo demanda (botón "refrescar") o watcher de
|
||||
filesystem? v1: botón refrescar (simple). Watcher si molesta.
|
||||
4. **Aristas dangling**: wikilinks a notas que aún no existen — ¿mostrarlos como nodos fantasma
|
||||
(útil para ver "objetivos pendientes de fichar") o esconderlos? Propuesta: nodo fantasma con
|
||||
estilo atenuado, toggle para ocultar.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
| Escenario | Tipo | Comando / evidencia | Resultado esperado |
|
||||
|---|---|---|---|
|
||||
| Golden: grafo carga el vault | e2e | `GET /api/graph` con el vault osint real | `nodes` ≥ nº de `.md`, `edges` con los wikilinks resueltos; sigma.js los pinta |
|
||||
| Golden: ficha con imágenes | e2e | `GET /api/node/<persona con fotos>` + abrir NodeCard | frontmatter + cuerpo + galería con las imágenes de `attachments/personas/<slug>/` |
|
||||
| Edge: tabla filtrada por tipo | e2e | `GET /api/nodes?tipo=organizacion` | solo nodos de ese tipo, columnas del frontmatter |
|
||||
| Edge: wikilink dangling | unit | nota con `[[Persona-Inexistente]]` | arista marcada dangling / nodo fantasma, sin crash |
|
||||
| Edge: nombre con mayúsculas/acentos | unit | wikilink `[[María del Mar]]` → slug | resuelve a `maria-del-mar-...md` vía `slugify_obsidian_name` |
|
||||
| Error: path traversal en attachment | e2e | `GET /api/attachment?path=../../etc/passwd` | 403/404, jamás sirve fuera del vault |
|
||||
| Error: vault inexistente | e2e | arrancar con `--vault /no/existe` | error claro al arrancar, no 500 silencioso |
|
||||
| Cobertura | audit | `uses_functions` del `app.md` | declara todas las funciones del grupo `obsidian` consumidas |
|
||||
|
||||
Vida útil (cuando se construya): usar la app de verdad sobre el vault osint durante ≥7 días en
|
||||
investigaciones reales; medir que el grafo sigue cargando sin romperse al crecer el vault.
|
||||
|
||||
## Notas
|
||||
|
||||
**Estado actual: solo plan.** No construir todavía — la recopilación de datos en Obsidian
|
||||
continúa; cuando el vault tenga masa crítica de objetivos/relaciones, se arranca con
|
||||
`/new-cpp-app` no aplica (es web): se hace `git init` del sub-repo `dataforge/osint_web` dentro de
|
||||
`projects/osint/apps/osint_web/` antes de limpiar cualquier worktree (regla `apps_subrepo.md`),
|
||||
scaffolding de frontend con el stack Mantine del registry, y backend Python orquestando el grupo
|
||||
`obsidian`.
|
||||
|
||||
Onboarding (para cuando exista): arrancar backend `python server/main.py --vault
|
||||
/home/enmanuel/Obsidian/osint --port 8470` y `pnpm dev` en `frontend/`; abrir
|
||||
`http://127.0.0.1:5173`. Pestañas: Grafo / Tablas / (ficha al click). Solo localhost por los
|
||||
datos sensibles del vault.
|
||||
|
||||
Relación con #0171 (manifest de sub-repos): cuando esta app exista será un hijo del project
|
||||
`osint` y debe entrar en su `subrepos.yaml` para re-clonarse en otros PCs.
|
||||
Reference in New Issue
Block a user