chore: snapshot WIP previo + flow 0008 + 7 sub-issues (0112-0119)

Snapshot de WIP acumulado de sesiones previas antes de merge wave 1
del flow 0008 (kanban_cpp + agent_runner_api + DoD schema).

Incluye:
- dev/flows/0008-kanban-cpp-and-agent-workflows.md
- dev/issues/0112-0119*.md (7 sub-issues)
- WIP previo en cmd/fn/doctor.go, registry/*, modules/, cpp/, etc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 18:17:08 +02:00
parent ddb5366884
commit b9716a7cd6
119 changed files with 14929 additions and 3084 deletions
+1
View File
@@ -258,6 +258,7 @@ fn check params # Lista funciones sin params_schema
fn doctor # Corre todos los checks
fn doctor artefacts # git/venv/app.md/upstream de cada app y analysis
fn doctor services # apps tag 'service' + systemctl + puerto
fn doctor services-spec # audita bloque `service:` del app.md (issue 0105)
fn doctor sync # drift pc_locations BD vs disco
fn doctor uses-functions # imports reales vs uses_functions del app.md
fn doctor unused # funciones del registry sin consumidores
+274
View File
@@ -0,0 +1,274 @@
# /cpp-app — Crear o modificar app C++ del registry sin olvidar nada
Recopila TODOS los datos necesarios (frontmatter, trio app_hub, panels, AppConfig, service block, e2e_checks, uses_functions) **antes** de tocar el disco. Tras confirmar, ejecuta scaffolder o edits, regenera iconos, refresca app_hub, compila y deploya a Windows.
Sustituye al flujo manual "edito main.cpp + app.md + CMakeLists.txt a mano". Wrapper sobre `init_cpp_app_bash_pipelines` (create) o edits directos sobre `app.md` (modify) + `regenerate_app_icons` + `refresh_app_hub` + `redeploy_cpp_app_windows`.
---
## Uso
```
/cpp-app # interactivo, modo create
/cpp-app <name> # interactivo, modo create con name pre-rellenado
/cpp-app modify <name> # editar app existente
```
---
## Modo CREATE — flujo turno a turno
Si `$ARGUMENTS` no empieza por `modify`, es create. Si trae `<name>`, lo usas como default; si no, pregunta name.
### Paso 0 — verificar que no existe
```bash
test -d "/home/lucas/fn_registry/apps/<name>" \
|| ls /home/lucas/fn_registry/projects/*/apps/<name> 2>/dev/null
```
Si existe en cualquier ubicacion: **abortar** y sugerir `/cpp-app modify <name>`. NO sobreescribir.
### Paso 1 — Identidad (AskUserQuestion)
1. **name** (texto libre — valida snake_case + contiene verbo segun `ids_naming.md`). Verbos canonicos: `show, render, view, plot, edit, manage, monitor, browse, explore, run, launch, scan, audit, debug, profile, ...`. Si no trae verbo, sugerir alternativas (`viewer` -> `<name>_viewer`).
2. **project** (select: ninguno / lista de `projects/*/`). Si ninguno -> `apps/<name>/`.
3. **domain** (select: `tools` (default), `gfx`, `tui`, `infra`, `finance`, `datascience`, `cybersecurity`, `shell`, `pipelines`, `browser`).
4. **description** 1 linea (texto libre, max 80 chars). **OBLIGATORIO** — sin esto el hub muestra tarjeta vacia.
### Paso 2 — Trio app_hub OBLIGATORIO
Regla dura `cpp_apps.md`: description + icon.phosphor + icon.accent SIEMPRE juntos.
5. **icon.phosphor** glyph name. Antes de preguntar, ofrece busqueda:
```bash
ls /home/lucas/fn_registry/sources/phosphor-core/assets/fill/ | grep -i "<keyword>"
```
Sugiere 3-5 candidatos basados en `description`. Default segun domain: `gfx`->`palette`, `tui`->`terminal`, `tools`->`wrench`, `infra`->`gear`, `finance`->`chart-line-up`, `datascience`->`graph`, `cybersecurity`->`shield`.
6. **icon.accent** hex `#rrggbb` (palette select):
- sky `#0ea5e9`, indigo `#4f46e5`, violet `#7c3aed`, pink `#ec4899`, rose `#f43f5e`, red `#dc2626`, orange `#ea580c`, amber `#d97706`, green `#16a34a`, teal `#0d9488`, cyan `#0891b2`, slate `#475569`. Default segun domain.
### Paso 3 — Tags
7. **tags** (multiSelect): `service`, `launcher`, `dashboard`, `viewer`, `editor`, `monitor`, `debug`, `prototype`. Si selecciona `service` -> activar bloque service (Paso 7).
### Paso 4 — Panels iniciales
8. **panels** (texto libre o select):
- Default: 1 panel `Main` (Ctrl+1).
- Opcion lista: hasta 4 paneles. Por cada uno: `{label, shortcut}`. Generara `PanelToggle k_panels[]` en `main.cpp`.
### Paso 5 — AppConfig flags
9. (multiSelect):
- `init_gl_loader` (true si la app llama `gl*` directo, ej. shaders, GPU renderer custom). Default false.
- `viewports` true (default) / false (single-window).
- `auto_dockspace` true (default) / false (solo si gestiona DockSpace propio tipo `shaders_lab`).
- `fps_overlay` activo de inicio? (controla solo el default; el menu Settings lo toggle).
### Paso 6 — Funciones del registry a usar
10. **uses_functions** lista IDs. Antes de preguntar, busca candidatas segun description:
```
mcp__registry__fn_search query="<keyword>" entity="functions"
```
Y muestra capability groups relevantes (`docs/capabilities/INDEX.md`). El usuario puede aceptar lista, anadir IDs, o dejar vacio (se rellena tras codear).
Cada ID que no este en el registry -> ofrecer spawn `fn-constructor` antes de continuar (regla `delegation.md`).
### Paso 7 — Bloque `service:` (solo si tag=service)
11. Si paso 3 marco `service`, recopilar (regla `function_tags.md` + issue 0105):
- `port` int o null
- `health_endpoint` ruta GET o null
- `health_timeout_s` (default 3)
- `runtime` (select: `systemd-user`, `systemd-system`, `docker-compose`, `stdio`, `manual`)
- `systemd_unit` (obligatorio si runtime empieza por `systemd-`)
- `systemd_scope` (`user|system|null`)
- `restart_policy` (select: `always` (Recommended — gotcha: `on-failure` NO reinicia SIGTERM limpio), `on-failure`, `none`)
- `pc_targets` (multiSelect de pc_locations actuales: `aurgi-pc`, `home-wsl`, ...)
- `is_local_only` (true/false default false)
### Paso 8 — Persistencia
12. (multiSelect):
- BD propia SQLite `<name>.db` en `local_files/`? -> recordar usar `fn::local_path("<name>.db")` (cpp_apps.md §7)
- operations.db (para entities/relations)? -> ejecutar `fn ops init` tras crear
- Archivos config en `local_files/`?
### Paso 9 — e2e_checks (issue 0068)
13. Default sugerido (modificable):
```yaml
e2e_checks:
- id: build
cmd: "cmake --build cpp/build --target <name> -j"
timeout_s: 300
- id: self_test
cmd: "./cpp/build/apps/<name>/<name> --self-test"
timeout_s: 30
severity: warning # si todavia no implementa --self-test
```
Pregunta: ¿anadir mas checks (ops_audit, pytest, smoke)?
### Paso 10 — Resumen y confirmacion
Mostrar bloque YAML completo del `app.md` que se va a generar + flags del scaffolder + post-acciones. Pedir confirmacion antes de ejecutar.
---
## Modo CREATE — ejecucion
Una vez confirmado:
```bash
cd /home/lucas/fn_registry
# 1. Scaffolder
./fn run init_cpp_app <name> \
[--project <p>] \
[--domain <d>] \
--desc "<description>" \
[--tags "<csv>"]
# 2. Editar app.md generado para anadir:
# - icon: {phosphor, accent}
# - service: {...} (si aplica)
# - uses_functions: [...]
# - e2e_checks: [...]
# (el scaffolder no rellena estos; editarlos con Edit tool)
# 3. Editar main.cpp generado para reflejar:
# - panels[] custom (si != default)
# - cfg.init_gl_loader / cfg.auto_dockspace / cfg.viewports
# - includes de funciones registry usadas
# 4. Editar CMakeLists.txt para anadir paths de funciones del registry:
# ${CMAKE_SOURCE_DIR}/functions/<d>/<f>.cpp
# 5. Si es service -> ofrecer crear systemd unit (skipear si runtime=stdio|manual)
# 6. Si pidio operations.db
./fn ops init apps/<name> # o projects/<p>/apps/<name>
# 7. Generar icono
./fn run generate_app_icon "<phosphor>" "<accent>" "<dir>/appicon.ico"
# 8. Indexar
./fn index
# 9. Compilar Windows
./fn run redeploy_cpp_app_windows <name> <dir> --build
# 10. Refrescar app_hub
./fn run refresh_app_hub
# 11. Auditoria
./fn doctor cpp-apps
[[ "<tag>" == *service* ]] && ./fn doctor services-spec
```
---
## Modo MODIFY — flujo
`/cpp-app modify <name>`
### Paso 0 — Localizar
```bash
# Buscar apps/<name>/ o projects/*/apps/<name>/
sqlite3 /home/lucas/fn_registry/registry.db \
"SELECT id, dir_path FROM apps WHERE name='<name>' AND lang='cpp';"
```
Si no existe: abortar, sugerir `/cpp-app` (sin args) para crear.
### Paso 1 — Mostrar config actual
```bash
mcp__registry__fn_show id="<id>"
cat <dir>/app.md
```
### Paso 2 — Que cambiar (multiSelect)
- `description` (1 linea)
- `icon.phosphor` o `icon.accent`
- `tags` (anadir/quitar; si toca `service` -> Paso 7 del create)
- `uses_functions` (anadir/quitar — recordar editar CMakeLists.txt)
- `panels` (anadir/quitar/renombrar)
- `service:` block (si tag=service)
- `e2e_checks`
- `domain`
- `rename` (cambia name, dir, IDs derivados, repo Gitea — operacion delicada, requiere doble confirmacion)
### Paso 3 — Aplicar cambios
Para cada cambio: usa `Edit` sobre los archivos correspondientes. NUNCA `Write` completo de `app.md` (preserva campos que no toques).
### Paso 4 — Post-acciones (segun lo que toco)
```bash
# Siempre
cd /home/lucas/fn_registry && ./fn index
# Si toco icon.* -> regenerar appicon
./fn run generate_app_icon "<phosphor>" "<accent>" "<dir>/appicon.ico"
# Si toco trio o panels o uses_functions o cambia code:
./fn run redeploy_cpp_app_windows <name> <dir> --build
# Si toco description o icon o tags:
./fn run refresh_app_hub
# Si toco service: o tag service
./fn doctor services-spec
# Siempre al final
./fn doctor cpp-apps
```
---
## Reglas duras
- **NUNCA** crear `main.cpp` + `CMakeLists.txt` + `app.md` a mano. Siempre via `init_cpp_app_bash_pipelines` (regla `cpp_apps.md`).
- **NUNCA** poner el codigo en `cpp/apps/<n>/`. Solo `apps/<n>/` o `projects/<p>/apps/<n>/`.
- **NUNCA** dejar `app.md` sin el trio (description + icon.phosphor + icon.accent). Tarjeta del hub queda gris.
- **NUNCA** declarar funciones del registry en `uses_functions` sin listar su `.cpp` en `CMakeLists.txt` (drift detectado por `fn doctor uses-functions`).
- **NUNCA** usar `Restart=on-failure` en systemd unit de un service C++ — gotcha 2026-05-17 (`sqlite_api.service` cayo 20h). Default `Restart=always`.
- Despues de **cualquier** cambio en el trio: `regenerate_app_icons <name>` + `refresh_app_hub`.
---
## Auto-verificacion final
Tras crear o modificar, reportar al usuario:
```
=== app <name> ===
dir: <abs_dir>
domain: <d>
description: "<desc>"
icon: <phosphor> + <accent>
tags: [<csv>]
uses_functions: N funciones (<list_top_5>)
panels: N (<labels>)
e2e_checks: N checks
service: <si/no — port:<p> health:<h>>
Acciones ejecutadas:
[✓] scaffolder / edits
[✓] generate_app_icon
[✓] fn index (registry.db actualizado)
[✓] redeploy_cpp_app_windows (Desktop/apps/<name>/<name>.exe)
[✓] refresh_app_hub (tarjeta visible en hub)
[✓] fn doctor cpp-apps (limpio | N warnings)
Siguiente paso sugerido:
- Abrir app_hub_launcher en Windows y verificar tarjeta
- Anadir tests visuales si la app tiene paneles propios (cpp/PATTERNS.md §11)
```
$ARGUMENTS
+186
View File
@@ -0,0 +1,186 @@
---
name: fix-issue
description: Implementar un issue de dev/issues/ end-to-end. Crea rama, ejecuta tareas, bumpa version si toca modulos/framework/apps (via /version), tests, cierra issue, integra a master.
---
# /fix-issue
Ejecuta el flujo completo de implementacion/cierre de un issue de `dev/issues/`. Adaptado al stack del registry: Go (`-tags fts5 CGO_ENABLED=1`), Python (`python/.venv/bin/python3`), Bash, TypeScript (`pnpm`), C++ (`cmake`+`mingw-w64` toolchain).
## Inputs
```
/fix-issue <NNNN[a|b|c...]>
```
- `NNNN`: numero del issue (ej. `0107`).
- Si es sub-issue, sufijo letra: `0107a`, `0107b`, ...
Si no se proporciona, preguntar.
## Flujo obligatorio
### 1. Resolver el issue
- `dev/issues/<NNNN>-*.md` → si no existe, STOP.
- Si ya en `dev/issues/completed/`, STOP.
- Si es sub-issue, leer tambien el principal para contexto.
### 2. Leer y extraer
- Objetivo, tareas, arquitectura, prerequisitos, riesgos.
- Identificar archivos afectados — anotar si toca:
- `modules/<X>/` o `cpp/framework/` → bumpa version (paso 8).
- `functions/`, `python/functions/`, `bash/functions/`, `frontend/functions/` → indexer + `fn index` al cerrar.
- Apps en `apps/<X>/` o `projects/*/apps/<X>/` → requiere rama TBD (regla `apps_tbd.md`) **+ bumpa version per-app (paso 8)**. Si el issue toca multiples apps, una llamada `/version` por app.
- Registry meta (CLAUDE.md, rules, templates) → push directo a master OK.
### 3. Estrategia de rama
**Registry-only changes** (functions/types/docs/rules):
- Push directo a master OK. NO crear rama.
**Apps changes** (apps/, projects/*/apps/):
- Crear rama TBD:
```bash
git checkout master
git pull --rebase
git checkout -b issue/<NNNN>-<slug>
```
La rama es del registry. Si la app es sub-repo, ademas crear rama dentro del sub-repo.
**Modules/framework changes** (`modules/`, `cpp/framework/`):
- Rama TBD obligatoria (afecta a todas las apps que linkean).
### 4. Plan con TaskCreate
- Crear tarea por bloque logico del issue.
- Incluir SIEMPRE:
- Tarea de tests (unit + smoke).
- Tarea de `fn index` si toco metadata.
- Tarea de `/version` si toco `modules/`, `cpp/framework/`, `apps/<X>/` o `projects/*/apps/<X>/` (una llamada por target).
- Tarea de cleanup/docs.
### 5. Implementar
Reglas registry-first (CLAUDE.md):
- ANTES de escribir codigo reutilizable → `mcp__registry__fn_search` para encontrar lo que existe.
- Si falta funcion reutilizable → spawn `fn-constructor` (no escribir inline).
- Si patron se repite >2x → propose nueva funcion.
- NUNCA `sqlite3 registry.db "SELECT ..."` plano — usar MCP.
Convenciones del stack:
| Stack | Build/test |
|---|---|
| Go | `CGO_ENABLED=1 go build -tags fts5 -o fn ./cmd/fn/` y `CGO_ENABLED=1 go test -tags fts5 ./...` |
| Python | `python/.venv/bin/python3 -m pytest <path>` |
| Bash | `bash -n <script>.sh` + tests inline |
| TypeScript | `cd frontend && pnpm build && pnpm test` |
| C++ (Linux) | `cmake --build build --target <app>` |
| C++ (Windows MinGW) | `cmake -B build/windows -DCMAKE_TOOLCHAIN_FILE=cpp/toolchains/mingw-w64.cmake && cmake --build build/windows --target <app>` |
Commits atomicos por bloque logico con prefijos: `feat:`, `fix:`, `test:`, `docs:`, `refactor:`, `chore:`. Mensajes en espanol. NO WIP.
### 6. Tests
Stack-dependent (ver arriba). Si tests pasan parcialmente con failures pre-existentes no causadas por la rama, documentar en cuerpo del commit/PR.
### 7. Feature flags (si aplica)
Si el issue forma parte de un feature multi-issue:
- Editar `dev/feature_flags.json` con el flag (desactivado).
- Activar el flag en el ultimo sub-issue del set.
Flag != WIP. Codigo detras de flag debe compilar + testear.
### 8. Version bump (si toco modulos/framework/apps)
**OBLIGATORIO si el issue toco** alguno de:
- `modules/<X>/` → bumpa `modules/<X>/module.md::version`.
- `cpp/framework/` → bumpa `modules/framework/module.md::version`.
- `apps/<X>/` → bumpa `apps/<X>/app.md::version`.
- `projects/<P>/apps/<X>/` → bumpa `projects/<P>/apps/<X>/app.md::version`.
```
/version <path> <major|minor|patch> "<reason>"
```
Reglas (modulos/framework):
- Major: breaking ABI/API publica.
- Minor: additive (nuevo helper, refactor interno sin cambio de API, nuevo miembro).
- Patch: bugfix puro.
Reglas (apps):
- Major: breaking observable (CLI args, schema BBDD propia, formato wire).
- Minor: feature aditiva visible (nuevo panel, endpoint, opcion).
- Patch: bugfix sin cambio observable, refactor interno, mejora perf.
**Una llamada `/version` por target afectado**. Si el issue toca 1 modulo + 2 apps -> 3 llamadas a `/version` (cada una con su `reason` y bump-type apropiado; pueden diferir).
Diff guard: cambios que solo tocan el `app.md` (correccion typo descripcion, anadir tag) NO requieren bump — son metadata, no comportamiento. Detectar con `git diff --name-only | grep -v '\.md$'` para decidir si hay cambio de codigo real.
`/version` solo edita + stage. NO commit. El bump va junto con el codigo correspondiente en el mismo commit (`feat:` o `fix:` o `refactor:`).
Si NO toco modulos/framework/apps, saltar este paso.
### 9. Cerrar el issue
Mover archivo:
```bash
mv dev/issues/<NNNN>-<slug>.md dev/issues/completed/
```
Actualizar `dev/issues/README.md`:
- Link → `completed/<NNNN>-<slug>.md`
- Estado → `completado`
Si es feature multi-issue y este es el ultimo sub-issue:
- Flip flag en `dev/feature_flags.json` a `enabled: true` con `enabled_at: <YYYY-MM-DD>`.
- Verificar que todos los sub-issues estan en `completed/`.
### 10. Integrar
**Registry-only changes**: push directo a master.
**Apps/modules/framework changes**: `/full-git-push` o `/git-push` (merge --no-ff de la rama a master, push, delete rama).
### 11. Verificar post-cierre
- `fn index` — registry.db al dia.
- `fn doctor` (subcomandos relevantes: `artefacts`, `services`, `cpp-apps`, `uses-functions`).
- Si toco modulos: `fn doctor modules` (post 0107a) — 0 drift.
## Reglas criticas
- **Registry-first**: SIEMPRE buscar antes de escribir; delegar a `fn-constructor` antes que inline.
- **TBD para apps**: NUNCA push directo a master en apps. Rama corta, merge --no-ff.
- **TBD NO para registry**: push directo OK para functions/types/docs/rules.
- **`/version` obligatorio** si tocas modulos, framework o apps (con cambio de codigo real, no solo metadata). Si no, drift entre `version:` y `## Capability growth log` y se pierde trazabilidad.
- **Tests siempre**: no cerrar issue sin tests pasando (salvo failures pre-existentes documentados).
- **Commits atomicos**: 1 commit = 1 bloque logico. No mezclar `feat:` + `test:` en mismo commit.
- **Cerrar siempre**: nunca dejar issue implementado sin mover a `completed/` + actualizar README.
## Referenciado desde
- `.claude/commands/version.md` — bump semver de modulos.
- `.claude/commands/full-git-push.md` — push del registry + sub-repos.
- `.claude/rules/apps_tbd.md` — politica de TBD por tipo de cambio.
## Ejemplo: implementar 0107c (refactor data_table)
```
/fix-issue 0107c
1. Resolver: dev/issues/0107c-split-data-table.md ✓
2. Extraer: refactor 4777 LOC → 6 sub-funciones. Toca modules/ → /version obligatorio.
3. Rama: issue/0107c-split-data-table desde master.
4. Plan: 8 tareas (lectura + 6 sub-funciones + entrypoint thin + version bump).
5. Implementar: spawn fn-constructor en paralelo si hay >1 sub-funcion independiente.
6. Tests: build + smoke + primitives_gallery --capture diff.
7. Flag: parte de modules-v2, NO activar todavia (espera 0107a-f cerrar).
8. /version modules/data_table major "split data_table.cpp into 6 sub-functions"
9. Cerrar: mv → completed/ + README.
10. /git-push.
11. fn index + fn doctor modules → 0 drift en consumidores limpiados.
```
+170
View File
@@ -0,0 +1,170 @@
---
name: version
description: Bumpear semver de un modulo, framework, paquete o app del registry. Edita <target>.md::version + ## Capability growth log. NO commitea.
---
# /version
Bumpea la version de un **modulo, framework, paquete o app** del registry siguiendo SemVer estricto y mantiene el `## Capability growth log` sincronizado con `<target>.md::version`.
Disenado para usarse desde `/fix-issue` cuando el cambio afecte:
- `modules/<X>/` (cualquier modulo C++) — edita `module.md`
- `cpp/framework/` — edita `modules/framework/module.md`
- `apps/<X>/` o `projects/<P>/apps/<X>/` — edita `app.md`
- Otros paquetes versionados con `<target>.md` y campo `version:`
## Inputs
```
/version <path> <major|minor|patch> "<reason>"
```
- `<path>`: directorio del target (ej. `modules/data_table`, `cpp/framework`, `apps/chart_demo`, `projects/fn_monitoring/apps/registry_dashboard`).
- `<major|minor|patch>`: tipo de bump SemVer.
- `<reason>`: 1-frase humana — lo que cambia. Se inserta en el log.
## Resolucion del archivo target
| Path empieza por | Archivo a editar |
|---|---|
| `modules/` | `<path>/module.md` |
| `cpp/framework` | `modules/framework/module.md` |
| `apps/` | `<path>/app.md` |
| `projects/*/apps/` | `<path>/app.md` |
| `projects/*/analysis/` | `<path>/analysis.md` |
Si no encuentra archivo target -> ERROR.
## Reglas SemVer
### Modulos / framework
| Bump | Cuando |
|---|---|
| `major` | Cambios breaking en API publica: firma de entry function, layout de State struct expuesto, eliminacion de members, cambio incompatible de comportamiento. |
| `minor` | Adiciones backwards-compatible: nuevo evento opt-in, nuevo renderer, nuevo helper, nuevo miembro. |
| `patch` | Bugfix sin cambio de API. |
Refactor interno SIN cambio de API publica -> `minor` (no major).
### Apps
| Bump | Cuando |
|---|---|
| `major` | Breaking observable por usuarios: CLI args incompatibles, schema BBDD propia rompe lectores viejos, formato wire (HTTP/gRPC) incompatible, eliminacion de panel/feature que la gente usaba. |
| `minor` | Feature aditiva: nuevo panel, nuevo endpoint, nueva opcion CLI, nueva tab, mejora visible no rompedora. |
| `patch` | Bugfix sin cambio observable. Refactor interno. Mejoras de perf. |
Bump de **dependencia** (modulo/funcion del registry) que mejora la app pero la app no cambia su API -> `patch` (la app no es responsable de la mejora; el modulo si).
## Flujo
### 1. Validar input
- `<target_file>` existe -> si no, ERROR.
- Bump type en {major, minor, patch} -> si no, ERROR.
- Reason no vacia -> si no, ERROR.
### 2. Leer version actual
Parsear frontmatter. Buscar `version: X.Y.Z`. Si no existe:
- Para `module.md` -> ERROR "module.md sin campo version".
- Para `app.md` -> asumir `0.1.0` (baseline) e insertar el campo despues de `domain:`.
### 3. Calcular proxima version
```
1.4.0 + major = 2.0.0
1.4.0 + minor = 1.5.0
1.4.0 + patch = 1.4.1
```
Major bump -> minor y patch a 0. Minor bump -> patch a 0.
### 4. Editar `<target_file>`
Cambiar linea `version: <old>` por `version: <new>`.
### 5. Anadir entrada a `## Capability growth log`
Insertar al inicio de la lista (lineas posteriores al header `## Capability growth log`):
```markdown
- v<new> (<fecha YYYY-MM-DD>) — <reason>
```
Si la seccion no existe -> crearla al final del archivo antes de `## Notes` (o al final si no hay Notes).
### 6. Verificar drift de members (solo modulos, opcional)
Si la herramienta `fn doctor modules` existe (post 0107a) y el target es modulo:
- Compara `members:` actual vs ultima version registrada en `registry.db::modules_history`.
- Si hay diff en members y bump es `patch` -> WARNING.
- Si hay diff en API publica y bump no es `major` -> ERROR (require `--force`).
No aplica a apps (no tienen `members:`).
### 7. Stage en git
`git add <target_file>`. NO commit. El commit final lo hace el flujo padre.
### 8. Reportar
```
/version apps/chart_demo minor "anade tab radar chart"
apps/chart_demo/app.md
version: 1.2.0 -> 1.3.0
## Capability growth log: + v1.3.0 (2026-05-18) — anade tab radar chart
Staged. NO committed.
Next: terminar el fix-issue y hacer commit con el resto de cambios.
```
## Reglas criticas
- **NUNCA commit**. `/version` solo edita + stage. El commit lo hace el flujo padre (`/fix-issue`, `/git-push`).
- **NUNCA saltar version**. No 1.4.0 -> 1.4.2 directo.
- **NUNCA bajar version**. Si rollback, crea nueva version superior con comportamiento viejo restaurado.
- **fecha = HOY** (`date +%Y-%m-%d`).
- **reason** comprensible sin contexto del PR actual.
## Referenciado desde
- `/fix-issue` — al detectar cambios en `modules/`, `cpp/framework/`, `apps/<X>/` o `projects/*/apps/<X>/`, sugiere ejecutar `/version` antes del commit final.
- `.claude/rules/cpp_apps.md` — politica de bump.
- `dev/issues/0107-modules-standardization.md` — origen del flujo (modulos).
## Ejemplos
```
# Bug fix en data_table (modulo)
/version modules/data_table patch "fix off-by-one en seleccion multi-row con shift+click"
# -> 1.4.0 -> 1.4.1
# Feature opt-in en framework
/version cpp/framework minor "anade cfg.auto_dockspace para overlay de paneles flotantes"
# -> 1.1.0 -> 1.2.0
# Feature en app C++
/version apps/chart_demo minor "anade tab radar chart con datos sinteticos"
# -> 1.2.0 -> 1.3.0
# Bug fix en app de proyecto
/version projects/fn_monitoring/apps/registry_dashboard patch "fix tooltip que mostraba duration_ms en segundos"
# -> 0.4.1 -> 0.4.2
# Breaking en app: cambia schema de su BBDD propia
/version apps/kanban major "cards.assignee_id pasa a ser TEXT[] (era TEXT); requiere migracion 008"
# -> 1.0.0 -> 2.0.0
```
## Anti-patrones
| Anti-patron | Por que es malo |
|---|---|
| Editar `version:` a mano sin `## Capability growth log` | Drift entre version y log; nadie sabe que cambio. |
| Bumpear major en app por refactor interno | Confunde al usuario; refactor es patch. |
| Patch para feature visible | Usuario no se entera que esta disponible. |
| Reason "cambios varios" / "mejoras" | Inutil para auditar. Una frase concreta. |
| Bump de app sin tocar codigo de la app (solo dep) | Bump va al modulo, no a la app. |
+24
View File
@@ -84,6 +84,7 @@ Plantilla minima para apps C++:
name: <name>
lang: cpp
domain: <gfx|tui|tools|infra|...>
version: 0.1.0 # semver per-app, bumped via /version
description: "Frase corta — lo que hace y por que existe."
tags: [imgui, ...] # si es service, anadir 'service'
uses_functions: # IDs del registry — el indexer NO deduce C++
@@ -102,6 +103,7 @@ Reglas:
- `framework: "imgui"` siempre que use `fn::run_app`. Otros valores solo si la app NO usa el shell (raro).
- `tags`: incluir `service` si es daemon de larga duracion (ver `function_tags.md`).
- `repo_url` apunta al sub-repo en Gitea (ver §6).
- `version`: semver per-app. Baseline `0.1.0` para apps nuevas. Bump obligatorio via `/version apps/<name> {major|minor|patch} "<reason>"` cuando `/fix-issue` toque codigo de la app. Trazabilidad humana en seccion `## Capability growth log` al final del `app.md` (una linea por bump). Ver `.claude/commands/version.md`.
### 5. Registro en `cpp/CMakeLists.txt`
@@ -385,11 +387,33 @@ generate_app_icon(
Mapping vive en el frontmatter de cada `app.md` C++:
```yaml
description: "Frase corta de 1 linea — que hace la app y por que existe."
icon:
phosphor: "chart-bar"
accent: "#0ea5e9"
```
### Trio obligatorio: description + icon.phosphor + icon.accent
**REGLA DURA:** TODA app C++/imgui declara los **3 campos JUNTOS** en su `app.md`:
1. `description:` (string corta, 1 linea) — texto que el `app_hub_launcher` muestra en la tarjeta y que el dashboard usa para tooltips.
2. `icon.phosphor:` (nombre del glyph Phosphor sin sufijo `-fill`) — glyph del icono.
3. `icon.accent:` (hex `#rrggbb`) — color del fondo redondeado del icono **Y** color del boton/border de la tarjeta en `app_hub_launcher`.
Los 3 se consumen como un set unico: el icono visual + el texto + el color de marca de la app. Una app sin descripcion aparece como tarjeta gris sin texto; sin `icon:` cae al default (`app-window` slate); sin accent el boton del hub aparece blanco. **Documentar uno sin los otros es bug**, no estilo.
### Refrescar el App Hub tras editar el trio
`app_hub_launcher` cachea iconos (PNG) y manifest (TSV) al arrancar. Cambiar `description`/`icon.*` en un `app.md` requiere regenerar ambos sidecars + relanzar el hub. Pipeline canonico:
```bash
./fn run refresh_app_hub # icons + manifest + restart hub
./fn run refresh_app_hub --no-restart # solo regenera, util si el hub esta cerrado
./fn run refresh_app_hub --size 128 # PNGs 128px en vez de 64
```
ID: `refresh_app_hub_bash_pipelines`. Compone `export_hub_icons_py_infra` + `export_hub_manifest_py_infra` + `is_cpp_app_running_windows_bash_infra` + `launch_cpp_app_windows_bash_infra`.
Regeneracion batch via pipeline del registry — escanea `app.md`s y compone
`generate_app_icon` por app. Anadir app nueva: declarar `icon:` en su
`app.md` y lanzar:
+23
View File
@@ -28,3 +28,26 @@ Documentar en el `app.md` del service:
- El puerto que usa (si expone HTTP/gRPC)
- Como lanzarlo y pararlo
- Como comprobar que esta vivo (health check)
### Bloque `service:` obligatorio (issue 0105)
Toda app con `tag: service` declara el bloque `service:` en su frontmatter. El indexer lo persiste en columnas dedicadas de `apps` + tabla `service_targets`. Consumido por `services_api`/`services_monitor` (issue 0106) y por `fn doctor services-spec`.
```yaml
service:
port: 8484 # null si no expone HTTP (stdio, daemon sin API)
health_endpoint: /api/databases # ruta GET, 2xx/3xx = sano; null si no aplica
health_timeout_s: 3
systemd_unit: sqlite_api.service # obligatorio si runtime empieza con `systemd-`
systemd_scope: user # user|system|null (docker-compose)
restart_policy: always # always|on-failure|none
runtime: systemd-user # systemd-user|systemd-system|docker-compose|stdio|manual
pc_targets: # >=1, pc_id de pc_locations
- aurgi-pc
- home-wsl
is_local_only: false # true => no se monitoriza por SSH (siempre local)
```
Validacion: `fn doctor services-spec` (`functions/infra/audit_services_spec.go`). Hoy 11/11 services con bloque completo.
**Gotcha critico:** usar `Restart=always` (no `on-failure`) en el unit systemd. Un `SIGTERM` limpio es exit success → `on-failure` NO reinicia y el service se queda muerto silenciosamente. `sqlite_api.service` cayo 20h asi el 2026-05-17.
@@ -0,0 +1,121 @@
#!/bin/bash
# integrate-worktrees.sh — Integra branches de worktrees a master con --no-ff
#
# Uso: ./integrate-worktrees.sh <slug-1> <slug-2> ...
# Ejemplo: ./integrate-worktrees.sh 0026-split-runtime 0027-prune-config-schema
#
# Para cada slug:
# 1. git merge --no-ff issue/<slug> a master
# 2. Verificar que master compila después del merge
# 3. Si hay conflict o fallo de build, PARAR inmediatamente
#
# Los slugs deben pasarse en el orden correcto (waves ya resueltas).
# NO hace push — eso lo decide el usuario.
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
if [ $# -eq 0 ]; then
echo "ERROR: se necesita al menos un slug"
echo "Uso: $0 <slug-1> <slug-2> ..."
exit 1
fi
# Asegurar que estamos en master
echo "=== Cambiando a master ==="
cd "$REPO_ROOT"
git checkout master
MERGED=0
FAILED_AT=""
for slug in "$@"; do
branch="issue/${slug}"
echo ""
echo "=== Integrando: ${branch} ==="
# Verificar que la branch existe
if ! git show-ref --verify --quiet "refs/heads/${branch}"; then
echo "FAIL: branch ${branch} no existe"
FAILED_AT="$slug"
break
fi
# Merge --no-ff
if ! git merge --no-ff "$branch" -m "merge: ${branch} — implementación paralela"; then
echo ""
echo "CONFLICT: merge de ${branch} tiene conflictos"
echo "Resolver manualmente y luego continuar con los slugs restantes"
echo ""
echo "Para resolver:"
echo " 1. git status (ver archivos en conflicto)"
echo " 2. Resolver conflictos en cada archivo"
echo " 3. git add <archivos>"
echo " 4. git commit"
echo ""
echo "Slugs pendientes después de ${slug}:"
FOUND=0
for remaining in "$@"; do
if [ "$FOUND" -eq 1 ]; then
echo " - ${remaining}"
fi
if [ "$remaining" = "$slug" ]; then
FOUND=1
fi
done
exit 1
fi
echo "MERGED: ${branch}"
# Verificar que master sigue compilando (si BUILD_CMD esta definido)
if [ -n "${BUILD_CMD:-}" ]; then
echo "--- Verificando build post-merge ($BUILD_CMD) ---"
if ! (cd "$REPO_ROOT" && bash -c "$BUILD_CMD" 2>&1); then
echo ""
echo "FAIL: master no compila despues de mergear ${branch}"
echo "Revertir con: git reset --hard HEAD~1"
echo "Investigar el problema antes de continuar."
FAILED_AT="$slug"
break
fi
echo "OK: build post-merge exitoso"
else
echo "--- Build post-merge SKIPPED (BUILD_CMD no definido) ---"
fi
MERGED=$((MERGED + 1))
done
echo ""
echo "=== Resumen de integración ==="
echo "Mergeados: ${MERGED} de $#"
if [ -n "$FAILED_AT" ]; then
echo "Falló en: ${FAILED_AT}"
echo ""
echo "Worktrees NO limpiados (resolver primero el fallo)"
exit 1
fi
# Limpieza de worktrees y branches
echo ""
echo "=== Limpieza ==="
for slug in "$@"; do
path="${REPO_ROOT}/worktrees/${slug}"
branch="issue/${slug}"
if [ -d "$path" ]; then
git worktree remove "$path" 2>/dev/null && echo "REMOVED: worktree ${path}" || echo "WARN: no se pudo eliminar worktree ${path}"
fi
git branch -d "$branch" 2>/dev/null && echo "DELETED: branch ${branch}" || echo "WARN: no se pudo eliminar branch ${branch}"
done
echo ""
echo "=== Integración completa ==="
echo "Master tiene ${MERGED} merges nuevos."
echo ""
echo "Para publicar: git push"
@@ -0,0 +1,76 @@
#!/bin/bash
# setup-worktrees.sh — Crea git worktrees para ejecución paralela de issues
#
# Uso: ./setup-worktrees.sh <slug-1> <slug-2> ...
# Ejemplo: ./setup-worktrees.sh 0026-split-runtime 0027-prune-config-schema
#
# Cada slug genera:
# worktrees/<slug>/ (worktree completo)
# branch: issue/<slug>
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
WORKTREE_DIR="${REPO_ROOT}/worktrees"
if [ $# -eq 0 ]; then
echo "ERROR: se necesita al menos un slug de issue"
echo "Uso: $0 <slug-1> <slug-2> ..."
exit 1
fi
# Asegurar que master está actualizado
echo "=== Actualizando master ==="
CURRENT_BRANCH="$(git branch --show-current)"
git checkout master 2>/dev/null
git pull --rebase 2>/dev/null || echo "WARN: no se pudo pull (sin remote o sin conexión)"
# Volver a la rama original si no era master
if [ "$CURRENT_BRANCH" != "master" ] && [ -n "$CURRENT_BRANCH" ]; then
git checkout "$CURRENT_BRANCH" 2>/dev/null
fi
mkdir -p "$WORKTREE_DIR"
CREATED=0
SKIPPED=0
FAILED=0
for slug in "$@"; do
branch="issue/${slug}"
path="${WORKTREE_DIR}/${slug}"
if [ -d "$path" ]; then
echo "SKIP: worktree ya existe: ${path}"
SKIPPED=$((SKIPPED + 1))
continue
fi
# Verificar que la branch no existe ya
if git show-ref --verify --quiet "refs/heads/${branch}" 2>/dev/null; then
echo "WARN: branch ${branch} ya existe, creando worktree desde ella"
git worktree add "$path" "$branch" 2>/dev/null || {
echo "FAIL: no se pudo crear worktree para ${slug}"
FAILED=$((FAILED + 1))
continue
}
else
echo "CREATE: worktree ${path} (branch ${branch})"
git worktree add -b "$branch" "$path" master 2>/dev/null || {
echo "FAIL: no se pudo crear worktree para ${slug}"
FAILED=$((FAILED + 1))
continue
}
fi
CREATED=$((CREATED + 1))
done
echo ""
echo "=== Resumen ==="
echo "Creados: ${CREATED}"
echo "Existentes: ${SKIPPED}"
echo "Fallidos: ${FAILED}"
echo ""
echo "=== Worktrees activos ==="
git worktree list
@@ -0,0 +1,165 @@
#!/bin/bash
# verify-worktree.sh — Verifica build, tests y cierre de issue en un worktree.
#
# Uso:
# ./verify-worktree.sh <worktree-path> [build-cmd] [test-cmd]
#
# Ejemplos:
# ./verify-worktree.sh worktrees/0026-foo
# ./verify-worktree.sh worktrees/0026-foo "go build -tags fts5 ./..." "go test -tags fts5 ./..."
# BUILD_CMD="cmake --build cpp/build" TEST_CMD="ctest --test-dir cpp/build" ./verify-worktree.sh worktrees/0026-foo
#
# Resolucion de comandos (en orden de prioridad):
# 1. Argumentos posicionales (build-cmd, test-cmd)
# 2. Variables de entorno BUILD_CMD / TEST_CMD
# 3. Archivo .parallel-fix-issues.yml en la raiz del worktree (claves: build, test)
# 4. Auto-deteccion segun ficheros del proyecto:
# - go.mod → "go build ./..." + "go test ./..."
# - CMakeLists.txt → "cmake -S . -B build && cmake --build build" + "ctest --test-dir build"
# - Cargo.toml → "cargo build" + "cargo test"
# - package.json → "npm run build" + "npm test"
# - pyproject.toml → "" + "pytest"
# 5. Si nada se detecta, salta build/test con WARN.
#
# Auto-deteccion adicional: si hay go.mod, intenta extraer build tag de //go:build.
#
# Exit codes:
# 0 = todo OK
# 1 = error de argumento
# 2 = build fallo
# 3 = tests fallaron
# 4 = issue no cerrado (solo WARN, no falla)
# 5 = sin commits propios
set -euo pipefail
if [ $# -lt 1 ]; then
echo "ERROR: se necesita el path del worktree"
echo "Uso: $0 <worktree-path> [build-cmd] [test-cmd]"
exit 1
fi
WORKTREE="$1"
ARG_BUILD_CMD="${2:-}"
ARG_TEST_CMD="${3:-}"
# Resolver path absoluto
if [[ "$WORKTREE" != /* ]]; then
REPO_ROOT="$(git rev-parse --show-toplevel)"
WORKTREE="${REPO_ROOT}/${WORKTREE}"
fi
if [ ! -d "$WORKTREE" ]; then
echo "ERROR: worktree no encontrado: ${WORKTREE}"
exit 1
fi
SLUG="$(basename "$WORKTREE")"
echo "=== Verificando: ${SLUG} ==="
# --- Resolver build/test commands ---
BUILD_CMD="${ARG_BUILD_CMD:-${BUILD_CMD:-}}"
TEST_CMD="${ARG_TEST_CMD:-${TEST_CMD:-}}"
# Manifest opcional
MANIFEST="${WORKTREE}/.parallel-fix-issues.yml"
if [ -z "$BUILD_CMD" ] && [ -f "$MANIFEST" ]; then
M_BUILD=$(grep -E "^build:" "$MANIFEST" 2>/dev/null | sed -E 's/^build:[[:space:]]*"?([^"]*)"?[[:space:]]*$/\1/' | head -1 || true)
if [ -n "$M_BUILD" ]; then BUILD_CMD="$M_BUILD"; echo "INFO: build desde manifest"; fi
fi
if [ -z "$TEST_CMD" ] && [ -f "$MANIFEST" ]; then
M_TEST=$(grep -E "^test:" "$MANIFEST" 2>/dev/null | sed -E 's/^test:[[:space:]]*"?([^"]*)"?[[:space:]]*$/\1/' | head -1 || true)
if [ -n "$M_TEST" ]; then TEST_CMD="$M_TEST"; echo "INFO: test desde manifest"; fi
fi
# Auto-deteccion
if [ -z "$BUILD_CMD" ] || [ -z "$TEST_CMD" ]; then
AUTO_BUILD=""
AUTO_TEST=""
if [ -f "${WORKTREE}/go.mod" ]; then
# Detectar build tag
AUTO_TAG=$(grep -rh "^//go:build " --include="*.go" "$WORKTREE" 2>/dev/null \
| sed -E 's|^//go:build ([a-zA-Z0-9_]+).*|\1|' \
| sort -u | head -1 || true)
TAG_FLAG=""
[ -n "$AUTO_TAG" ] && TAG_FLAG="-tags $AUTO_TAG"
AUTO_BUILD="go build $TAG_FLAG ./..."
AUTO_TEST="go test $TAG_FLAG ./..."
echo "INFO: stack detectado: Go${TAG_FLAG:+ ($TAG_FLAG)}"
elif [ -f "${WORKTREE}/CMakeLists.txt" ] || ls "${WORKTREE}"/cpp/CMakeLists.txt >/dev/null 2>&1; then
CMAKE_DIR="."
[ -f "${WORKTREE}/cpp/CMakeLists.txt" ] && [ ! -f "${WORKTREE}/CMakeLists.txt" ] && CMAKE_DIR="cpp"
AUTO_BUILD="cmake -S ${CMAKE_DIR} -B ${CMAKE_DIR}/build -DCMAKE_BUILD_TYPE=Release && cmake --build ${CMAKE_DIR}/build -j"
AUTO_TEST="ctest --test-dir ${CMAKE_DIR}/build --output-on-failure || true"
echo "INFO: stack detectado: C++/CMake (dir=${CMAKE_DIR})"
elif [ -f "${WORKTREE}/Cargo.toml" ]; then
AUTO_BUILD="cargo build"
AUTO_TEST="cargo test"
echo "INFO: stack detectado: Rust"
elif [ -f "${WORKTREE}/package.json" ]; then
AUTO_BUILD="npm run build --if-present"
AUTO_TEST="npm test --if-present"
echo "INFO: stack detectado: Node"
elif [ -f "${WORKTREE}/pyproject.toml" ] || [ -f "${WORKTREE}/setup.py" ]; then
AUTO_BUILD="" # python normalmente no tiene build step
AUTO_TEST="pytest"
echo "INFO: stack detectado: Python"
else
echo "WARN: no se detecto stack; usar BUILD_CMD/TEST_CMD env o manifest .parallel-fix-issues.yml"
fi
[ -z "$BUILD_CMD" ] && BUILD_CMD="$AUTO_BUILD"
[ -z "$TEST_CMD" ] && TEST_CMD="$AUTO_TEST"
fi
# 1. Verificar commits propios
echo ""
echo "--- Commits propios ---"
COMMIT_COUNT=$(cd "$WORKTREE" && git log master..HEAD --oneline 2>/dev/null | wc -l)
if [ "$COMMIT_COUNT" -eq 0 ]; then
echo "FAIL: sin commits propios en la branch"
exit 5
fi
echo "OK: ${COMMIT_COUNT} commits desde master"
cd "$WORKTREE" && git log master..HEAD --oneline
# 2. Build
echo ""
if [ -n "$BUILD_CMD" ]; then
echo "--- Build ($BUILD_CMD) ---"
if (cd "$WORKTREE" && bash -c "$BUILD_CMD" 2>&1); then
echo "OK: build exitoso"
else
echo "FAIL: build fallo"
exit 2
fi
else
echo "--- Build SKIPPED (sin comando) ---"
fi
# 3. Tests
echo ""
if [ -n "$TEST_CMD" ]; then
echo "--- Tests ($TEST_CMD) ---"
if (cd "$WORKTREE" && bash -c "$TEST_CMD" 2>&1); then
echo "OK: tests pasaron"
else
echo "FAIL: tests fallaron"
exit 3
fi
else
echo "--- Tests SKIPPED (sin comando) ---"
fi
# 4. Issue cerrado
echo ""
echo "--- Cierre de issue ---"
COMPLETED_FILES=$(cd "$WORKTREE" && git diff --name-only master -- dev/issues/completed/ 2>/dev/null | wc -l)
if [ "$COMPLETED_FILES" -gt 0 ]; then
echo "OK: issue movido a completed/"
cd "$WORKTREE" && git diff --name-only master -- dev/issues/completed/
else
echo "WARN: no se detecto issue movido a completed/ (verificar manualmente)"
fi
echo ""
echo "=== RESULTADO: ${SLUG} — OK ==="