feat(0130): kanban_cpp v2 — backend Go + 5 registry parser fns + epic/sub-issues

Registry (issue 0130a):
- 5 fns infra: parse_issue_md, write_issue_md, scan_issues_dir,
  scan_flows_dir, watch_dir_fsnotify
- 3 tipos: Issue, Flow, FsEvent
- Tests round-trip + scan reales + watcher fsnotify (all PASS)
- Capability group 'kanban' nuevo (docs/capabilities/kanban.md)

Apps:
- apps/kanban_cpp/ (sub-repo) — frontend ImGui: board drag-drop,
  flows, filters, detail con CSV editors
- apps/kanban_cpp/backend/ — Go service port 8487: REST + SSE +
  fsnotify watcher, parser bidireccional MD<->SQLite cache

Issues:
- dev/issues/0130-kanban-cpp-v2.md (epic)
- 0130a parser, 0130b backend, 0130c frontend

CMakeLists.txt: add_subdirectory apps/kanban_cpp (registrado por
init_cpp_app scaffolder).

End-to-end verde: backend devuelve 189 issues + 9 flows; PATCH a
/api/issues/{id} reescribe .md (solo frontmatter, body intacto);
frontend --self-test exit 0; tests Go infra 5/5 PASS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Egutierrez
2026-05-22 22:20:15 +02:00
parent e387c91b4c
commit c468b24d2b
31 changed files with 1706 additions and 9 deletions
+1
View File
@@ -40,6 +40,7 @@ Indice de grupos de capacidades del registry. Cada grupo agrupa >=3 funciones qu
| [cpp-dashboard-viz](cpp-dashboard-viz.md) | 10 | Primitivas C++ ImGui para dashboards: kpi_card, sparkline, line/bar/scatter/pie/heatmap/histogram, panel containers |
| [agents](agents.md) | 3 | Orquestar agentes Claude headless en git worktrees: launch, cleanup, DoD evidence schema audit |
| [backends](backends.md) | — | Stacks backend (Go net/http+SQLite default, MCP, mautrix, bubbletea, httpx, docker-compose): decision tree + esqueleto canonico + funciones del registry a componer |
| [kanban](kanban.md) | 5 | Parser/writer/scanner/watcher de dev/issues/ y dev/flows/: base del backend kanban_cpp v2 |
## Como anadir grupo
+68
View File
@@ -0,0 +1,68 @@
# kanban — Parser/writer de issues y flows del registry
Cluster de funciones para leer, escribir y vigilar los archivos `dev/issues/*.md` y `dev/flows/*.md`. Base del backend de `kanban_cpp v2` (issue 0130b) y de cualquier herramienta que opere sobre el board de desarrollo.
## Funciones
| ID | Firma corta | Que hace |
|---|---|---|
| `parse_issue_md_go_infra` | `(path) → (Issue, []byte, error)` | Lee un .md de issue, extrae frontmatter YAML + body |
| `write_issue_md_go_infra` | `(path, Issue, body) → error` | Serializa Issue a YAML y reescribe el .md preservando body |
| `scan_issues_dir_go_infra` | `(root) → ([]Issue, error)` | Escanea dev/issues/ + completed/, devuelve todos los Issues ordenados |
| `scan_flows_dir_go_infra` | `(root) → ([]Flow, error)` | Escanea dev/flows/, devuelve todos los Flows ordenados |
| `watch_dir_fsnotify_go_infra` | `(ctx, root) → (<-chan FsEvent, error)` | Watcher recursivo con debounce 200ms, emite FsEvent por cambio |
## Tipos
| ID | Que es |
|---|---|
| `issue_go_infra` | Frontmatter de dev/issues/*.md: id, title, status, domain, priority, depends, blocks… |
| `flow_go_infra` | Frontmatter de dev/flows/*.md: id, name/title, status, kind, tags |
| `fs_event_go_infra` | Evento de watcher: {Path, Op} donde Op ∈ {create, write, remove, rename} |
## Ejemplo canónico — arrancar el backend de kanban_cpp
```go
import "fn-registry/functions/infra"
const (
issuesDir = "/home/lucas/fn_registry/dev/issues"
flowsDir = "/home/lucas/fn_registry/dev/flows"
)
// 1. Carga inicial
issues, _ := infra.ScanIssuesDir(issuesDir)
flows, _ := infra.ScanFlowsDir(flowsDir)
fmt.Printf("%d issues, %d flows cargados\n", len(issues), len(flows))
// 2. Actualizar status in-place
iss, body, _ := infra.ParseIssueMd(issuesDir + "/0130-kanban-cpp-v2.md")
iss.Status = "in-progress"
iss.Updated = "2026-05-22"
infra.WriteIssueMd(iss.FilePath, iss, body)
// 3. Vigilar cambios externos (editor de texto, otro agente)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ch, _ := infra.WatchDirFsnotify(ctx, issuesDir)
for ev := range ch {
if strings.HasSuffix(ev.Path, ".md") {
updated, _, _ := infra.ParseIssueMd(ev.Path)
cache.Upsert(updated) // invalidar cache SQLite
}
}
```
## Fronteras
- NO incluye markdown rendering del body (eso lo hace el frontend).
- NO valida campos contra TAXONOMY (existe `fn doctor issues`).
- NO crea ni borra archivos de issue (solo lee/escribe los existentes).
- NO incluye endpoints HTTP ni SSE (eso es el backend de la app, issue 0130b).
## Notas
- `parse_issue_md` + `write_issue_md` son el par CRUD atómico. Siempre usarlos juntos.
- `scan_issues_dir` llama a `parse_issue_md` internamente — no reimplementar el walk.
- `watch_dir_fsnotify` emite eventos para cualquier archivo, no solo `.md`. Filtrar por extensión en el consumidor.
- El watcher y el writer pueden producir loops: el writer dispara un evento `write` que el watcher emite. El backend debe ignorar eventos generados por sus propios writes (comparar path + timestamp).