## Apps son sub-repos Gitea independientes — gotcha al usar worktrees **Regla operativa critica** descubierta el 2026-05-18 durante implementacion del flow 0008. ### El gotcha `apps/*/` esta en `.gitignore` del repo `fn_registry`. Cada app es **su propio repo Gitea** en `dataforge/` con su `.git/` dentro de `apps//`. Esto significa: - Cuando un agente trabaja en un git **worktree** del repo padre y crea `apps//`, los archivos viven SOLO en el working directory del worktree. - Como `apps/*/` esta gitignored en el repo padre, los archivos **no se pueden commitear** al worktree del repo padre. - Cuando se hace `git worktree remove --force worktrees//`, el working directory entero se borra — **el codigo de la app desaparece**. **Consecuencia**: una app creada dentro de un worktree del repo padre se pierde al limpiar el worktree salvo que se haya promovido a su propio sub-repo Gitea ANTES. ### El patron correcto al crear apps en worktrees ```bash # 1. Agente trabaja en worktree del repo padre cd $HOME/fn_registry/worktrees/ # 2. Scaffold la app via pipeline canonico ./fn run init_cpp_app # apps C++ # o ./fn run init_jupyter_analysis ... # analysis # o crear apps// a mano (Go service, etc.) # 3. ANTES de salir del worktree: inicializa la app como sub-repo cd apps/ git init -b master git add -A git -c user.email="agent@fn_registry" -c user.name="agent" \ commit -m "feat: initial scaffold of " # 4. Trabajo continua en sub-repo (commits dentro de apps//.git) # 5. Cerrar issue en repo padre (mv .md a completed/), commit del padre con cambios en cpp/CMakeLists.txt, etc. ``` Cuando el humano corre `/full-git-push` despues del merge, el script `ensure_repo_synced_bash_infra` detecta que `apps//.git` existe + no tiene remote + crea repo Gitea en `dataforge/` + pushea master. ### Que ESTA SI versionado en el repo padre - `cpp/CMakeLists.txt` (el `if(EXISTS ...) add_subdirectory(apps/) endif()`). - `dev/issues/completed/-.md` (cierre del issue). - `docs/capabilities/*.md` si la app aporta a un capability group. - `dev/feature_flags.json` si introduce flags. Todo lo demas (codigo de la app + app.md + appicon + service unit + tests propios de la app) vive en `apps//.git` independiente. ### REGLA DURA: el repo padre NUNCA trackea contenido de artefactos hijos El repo padre `fn_registry` solo versiona codigo del registry (`functions/`, `types/`, `registry/`, `cmd/`, `docs/`, `.claude/`, `dev/`, `migrations/`, y el framework/functions/vendor de `cpp/`). NUNCA debe trackear el contenido de un artefacto hijo: - apps: `apps/*`, `cpp/apps/*`, `projects/*/apps/*` - analyses: `analysis/*`, `projects/*/analysis/*` - projects: `projects/*` Cada artefacto es un sub-repo Gitea independiente con su propio `.git`; su contenido completo (codigo, `app.md`, `analysis.md`, `appicon.*`, binarios, frontend, `local_files/`, tests propios) vive SOLO en ese sub-repo. `fn index` lee los `.md` de registro directamente del disco — no necesitan estar en el git del padre. Lo unico que el padre versiona dentro de esos arboles son los marcadores `.gitkeep` (mantienen `apps/` y `analysis/` presentes cuando estan vacios) y, en `projects/`, los `project.md` template si los hubiera. **Como se rompe (sintoma = repo padre permanentemente dirty):** un `git add -f apps//...` (forzado, saltandose el `.gitignore`) o un commit que mete contenido del hijo al padre. Como el archivo ya queda en el indice, el `.gitignore` NO lo vuelve a ignorar y aparece para siempre en `git status` del padre como modificado cada vez que el sub-repo cambia (doble-tracking). Caso real (2026-06-03): `apps/dag_engine/` (31 archivos: Go + frontend + app.md) y `apps/shaders_lab/` (app.md + un binario `.exe` de 23 MB) quedaron forzados al indice del padre y lo dejaban dirty en cada cambio del sub-repo. **Auditoria (cero salida = sano):** ```bash git ls-files 'apps/*' 'analysis/*' 'projects/*/apps/*' 'projects/*/analysis/*' 'cpp/apps/*' \ | grep -vE '(^|/)\.gitkeep$' ``` **Fix si aparece contenido trackeado:** ```bash # --cached SIEMPRE: saca del indice del padre sin borrar el working tree. # El codigo sigue a salvo en el .git del sub-repo. git rm -r --cached apps/ git commit -m "chore: untrack contenido del artefacto (es sub-repo Gitea)" ``` NUNCA `git rm` sin `--cached` (borraria el working tree del sub-repo). **Prevencion:** jamas usar `git add -f` sobre paths de artefactos; las reglas `apps/*/`, `analysis/*/`, `projects/*/` del `.gitignore` ya cubren el caso por defecto y solo un force las salta. ### Sintomas de la perdida Si limpias el worktree y luego corres `ls apps//`, devuelve "No such file or directory" pese a que el issue aparece cerrado en `dev/issues/completed/`. **Patron** = scaffold sin sub-repo init = trabajo perdido. ### Recovery si pasa 1. Re-crear worktree desde master. 2. Re-spawn agente con instruccion explicita: **`git init` dentro de la app antes de terminar**. 3. NO eliminar el worktree hasta confirmar que `apps//.git` esta inicializado con al menos un commit. ### Aplica tambien a analysis `analysis/*/` y `projects/*/analysis/*/` siguen mismo patron (cada analysis es repo Gitea). El pipeline `init_jupyter_analysis_bash_pipelines` ya hace `git init` automatico — por eso no hubo perdidas alli. Las apps C++/Go scaffolded a mano NO inicializan el sub-repo automaticamente — es responsabilidad del agente. ### Lo que aprende `parallel-fix-issues` El template del prompt de cada agente DEBE incluir la instruccion: > "Si tu issue crea una app nueva en `apps//`, inicializa el sub-repo (`cd apps/ && git init -b master && git add -A && git commit ...`) antes de terminar. Sin esto, `apps/*` esta gitignored y el codigo se perdera cuando el orquestador limpie el worktree." Aplicar este parrafo al template del skill — ver `.claude/skills/parallel-fix-issues/SKILL.md` (o equivalente). ### Relacion con otras reglas - [[apps_tbd]] — TBD en apps, esta regla complementa con el patron de sub-repo init. - [[artefactos]] — apps son artefactos, esta regla especifica gotcha de su sub-repo. - [[apps_vs_functions]] — apps en `apps/`, esta regla refuerza por que apps/* gitignored.