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:
@@ -1,22 +1,28 @@
|
||||
---
|
||||
id: "0130"
|
||||
title: "Kanban C++ v2 — gestor de dev/issues y dev/flows con backend Go + frontend ImGui"
|
||||
title: Kanban C++ v2 — gestor de dev/issues y dev/flows con backend Go + frontend ImGui
|
||||
status: pendiente
|
||||
type: epic
|
||||
domain:
|
||||
- cpp-stack
|
||||
- apps-infra
|
||||
- dev-ux
|
||||
- cpp-stack
|
||||
- apps-infra
|
||||
- dev-ux
|
||||
scope: multi-app
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks: []
|
||||
related:
|
||||
- "0112"
|
||||
- "0119"
|
||||
created: 2026-05-22
|
||||
updated: 2026-05-22
|
||||
tags: [kanban, cpp, imgui, dev_ux, issues, flows]
|
||||
- "0112"
|
||||
- "0119"
|
||||
tags:
|
||||
- kanban
|
||||
- cpp
|
||||
- imgui
|
||||
- dev_ux
|
||||
- issues
|
||||
- flows
|
||||
created: "2026-05-22"
|
||||
updated: "2026-05-22"
|
||||
---
|
||||
|
||||
# 0130 — Kanban C++ v2
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 0130a
|
||||
title: 'Funciones registry: parser MD + scan dirs + writer + watcher'
|
||||
status: pendiente
|
||||
type: infra
|
||||
domain:
|
||||
- registry-quality
|
||||
- dev-ux
|
||||
scope: registry-only
|
||||
priority: alta
|
||||
depends: []
|
||||
blocks:
|
||||
- 0130b
|
||||
related:
|
||||
- "0130"
|
||||
tags:
|
||||
- registry
|
||||
- go
|
||||
- parser
|
||||
- frontmatter
|
||||
- fsnotify
|
||||
flow: "0130"
|
||||
created: "2026-05-22"
|
||||
updated: "2026-05-22"
|
||||
---
|
||||
|
||||
# 0130a — Funciones registry para kanban_cpp v2
|
||||
|
||||
**Status:** pendiente
|
||||
|
||||
## Por que
|
||||
|
||||
El backend de kanban_cpp v2 necesita parsear/escribir frontmatter YAML de los `.md` de `dev/issues/` y `dev/flows/`. Estas piezas son reusables (cualquier app del registry puede operar sobre issues/flows), asi que viven en el registry, no en el backend de la app.
|
||||
|
||||
## Funciones a crear (delegar a fn-constructor en paralelo)
|
||||
|
||||
| ID | Firma | Pureza |
|
||||
|---|---|---|
|
||||
| `parse_issue_md_go_infra` | `(path string) (Issue, []byte body, error)` | impure (FS) |
|
||||
| `write_issue_md_go_infra` | `(path string, issue Issue, body []byte) error` | impure (FS) |
|
||||
| `scan_issues_dir_go_infra` | `(root string) ([]Issue, error)` | impure (FS) |
|
||||
| `scan_flows_dir_go_infra` | `(root string) ([]Flow, error)` | impure (FS) |
|
||||
| `watch_dir_fsnotify_go_infra` | `(ctx, root) (<-chan FsEvent, error)` | impure (FS, async) |
|
||||
|
||||
Tipos:
|
||||
- `Issue_go_infra` — struct con campos del frontmatter (id, title, status, type, domain, scope, priority, depends, blocks, related, flow, tags, created, updated, file_path, mtime_ns).
|
||||
- `Flow_go_infra` — struct equivalente para flows.
|
||||
- `FsEvent_go_infra` — `{path, op}` con `op in {create, write, remove, rename}`.
|
||||
|
||||
## Notas de implementacion
|
||||
|
||||
- Usar `gopkg.in/yaml.v3` para parsing (preserva orden de keys via `yaml.Node`).
|
||||
- Writer DEBE preservar:
|
||||
- Orden de campos del frontmatter original.
|
||||
- Body MD intacto (todo lo que va despues del segundo `---`).
|
||||
- Comentarios YAML (libre, best-effort).
|
||||
- `parse_issue_md` debe ser tolerante: si falta un campo opcional, default empty.
|
||||
- `watch_dir_fsnotify` recursivo, debounce 200ms.
|
||||
|
||||
## DoD
|
||||
|
||||
- 5 pares `.go` + `.md` en `functions/infra/`.
|
||||
- Tests unitarios:
|
||||
- parse → write → parse round-trip preserva struct.
|
||||
- scan_issues_dir devuelve >=90 issues actuales.
|
||||
- watcher detecta creacion + modificacion + borrado.
|
||||
- `fn index` registra los 5 IDs + 3 tipos.
|
||||
- `fn doctor uses-functions` limpio.
|
||||
|
||||
## Anti-scope
|
||||
|
||||
NO incluye en esta tanda:
|
||||
- Markdown rendering del body (eso lo hace el frontend si quiere).
|
||||
- Validacion contra TAXONOMY (existe `fn doctor issues`).
|
||||
- CRUD de issues nuevos (write_issue cubre el caso, pero crear file = scope del backend).
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
id: "0130b"
|
||||
title: "Backend Go kanban_cpp v2: schema + handlers + watcher + SSE"
|
||||
status: pendiente
|
||||
type: app
|
||||
domain:
|
||||
- apps-infra
|
||||
- dev-ux
|
||||
scope: app-scoped
|
||||
priority: alta
|
||||
depends:
|
||||
- "0130a"
|
||||
blocks:
|
||||
- "0130c"
|
||||
related:
|
||||
- "0130"
|
||||
created: 2026-05-22
|
||||
updated: 2026-05-22
|
||||
tags: [service, kanban, go, sqlite, sse]
|
||||
flow: "0130"
|
||||
---
|
||||
|
||||
# 0130b — Backend Go kanban_cpp v2
|
||||
|
||||
**Status:** pendiente
|
||||
|
||||
## Por que
|
||||
|
||||
Servicio HTTP local que sirve los issues + flows del proyecto al frontend C++. Es un wrapper fino sobre las funciones del registry de 0130a + SQLite cache + watcher.
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
apps/kanban_cpp/backend/
|
||||
app.md # tag service
|
||||
go.mod
|
||||
main.go # entry: flags + run
|
||||
db.go # open + apply migrations + upsert helpers
|
||||
handlers.go # endpoints REST
|
||||
sse_hub.go # broadcaster
|
||||
watcher.go # bind a watch_dir_fsnotify + re-ingesta + emit SSE
|
||||
ingest.go # scan → upsert; usa 0130a
|
||||
migrations/
|
||||
001_init.sql
|
||||
operations.db # creada en runtime
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Verbo | Path | Notas |
|
||||
|---|---|---|
|
||||
| GET | `/api/health` | `{ok:true, version, count_issues, count_flows}` |
|
||||
| GET | `/api/issues` | filtros: `status`, `domain`, `priority`, `tag`, `scope` |
|
||||
| GET | `/api/issues/{id}` | issue + body |
|
||||
| PATCH | `/api/issues/{id}` | partial update frontmatter → `write_issue_md` + re-ingesta + SSE |
|
||||
| GET | `/api/flows` | filtros: `status`, `kind` |
|
||||
| GET | `/api/flows/{id}` | flow + body |
|
||||
| GET | `/api/meta` | enums leidos de `dev/TAXONOMY.md` |
|
||||
| GET | `/api/sse` | stream `{type, id, path}` |
|
||||
|
||||
CORS abierto local (`*`). Logger middleware.
|
||||
|
||||
## Schema (migrations/001_init.sql)
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS issues (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
type TEXT,
|
||||
scope TEXT,
|
||||
priority TEXT,
|
||||
domain_json TEXT NOT NULL DEFAULT '[]',
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
depends_json TEXT NOT NULL DEFAULT '[]',
|
||||
blocks_json TEXT NOT NULL DEFAULT '[]',
|
||||
related_json TEXT NOT NULL DEFAULT '[]',
|
||||
flow_id TEXT,
|
||||
body TEXT NOT NULL DEFAULT '',
|
||||
file_path TEXT NOT NULL,
|
||||
mtime_ns INTEGER NOT NULL,
|
||||
created_at TEXT,
|
||||
updated_at TEXT,
|
||||
completed INTEGER NOT NULL DEFAULT 0 -- 1 si vive en completed/
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_issues_status ON issues(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_issues_priority ON issues(priority);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS flows (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
status TEXT,
|
||||
kind TEXT,
|
||||
tags_json TEXT NOT NULL DEFAULT '[]',
|
||||
body TEXT NOT NULL DEFAULT '',
|
||||
file_path TEXT NOT NULL,
|
||||
mtime_ns INTEGER NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
## DoD
|
||||
|
||||
- `curl http://localhost:8487/api/health` devuelve 200 + counts.
|
||||
- `curl http://localhost:8487/api/issues | jq 'length' >= 90`.
|
||||
- `curl -X PATCH /api/issues/0130 -d '{"status":"in-progress"}'` reescribe `dev/issues/0130-*.md` (status updated, body intacto).
|
||||
- Despues del PATCH, suscriptor SSE recibe evento `{type:"updated", id:"0130"}`.
|
||||
- Tras `mv dev/issues/0130-*.md dev/issues/completed/`, watcher actualiza fila (`completed=1`).
|
||||
- `go test ./...` verde.
|
||||
|
||||
## Anti-scope
|
||||
|
||||
- No expone proposals ni capabilities (eso es MCP registry).
|
||||
- No autentica (local-only por ahora).
|
||||
- No persiste estado UI (eso lo hace el frontend).
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
id: "0130c"
|
||||
title: "Frontend C++ ImGui kanban_cpp v2: board + flows + filtros + detalle"
|
||||
status: pendiente
|
||||
type: app
|
||||
domain:
|
||||
- cpp-stack
|
||||
- dev-ux
|
||||
scope: app-scoped
|
||||
priority: alta
|
||||
depends:
|
||||
- "0130b"
|
||||
blocks: []
|
||||
related:
|
||||
- "0130"
|
||||
created: 2026-05-22
|
||||
updated: 2026-05-22
|
||||
tags: [cpp, imgui, kanban, frontend]
|
||||
flow: "0130"
|
||||
---
|
||||
|
||||
# 0130c — Frontend C++ ImGui kanban_cpp v2
|
||||
|
||||
**Status:** pendiente
|
||||
|
||||
## Por que
|
||||
|
||||
UI nativa sobre el backend 0130b. Aprovecha el framework `fn::run_app` (menubar, layouts, settings, about, log) y los componentes del registry (`data_table`, `kpi_card`, `http_request`, `sse_client`).
|
||||
|
||||
## Estructura
|
||||
|
||||
```
|
||||
apps/kanban_cpp/
|
||||
app.md
|
||||
appicon.ico
|
||||
CMakeLists.txt
|
||||
main.cpp # fn::run_app + cfg.panels
|
||||
data.h / data.cpp # http client + state global (issues, flows, filters)
|
||||
panel_board.cpp # 4 columnas + drag-drop
|
||||
panel_flows.cpp # tabla via data_table_cpp_viz
|
||||
panel_filters.cpp # Aside con multi-select
|
||||
panel_detail.cpp # form editable del issue seleccionado
|
||||
panels.h
|
||||
```
|
||||
|
||||
## Trio obligatorio (`app.md`)
|
||||
|
||||
```yaml
|
||||
description: "Kanban C++ v2 para gestionar dev/issues y dev/flows del registry"
|
||||
icon:
|
||||
phosphor: "kanban"
|
||||
accent: "#a855f7"
|
||||
```
|
||||
|
||||
## Paneles
|
||||
|
||||
1. **Board** (`TI_KANBAN " Board"`) — 4 columnas (pendiente / in-progress / bloqueado / completado). Cada card: id + title (trunc 60) + priority badge + first domain chip. Drag-drop con `ImGui::BeginDragDropSource/Target` -> PATCH status.
|
||||
2. **Flows** (`TI_FLOW " Flows"`) — `data_table_cpp_viz` con columnas id/title/status/kind. Click fila → carga detail.
|
||||
3. **Filters** (`TI_FUNNEL " Filters"`) — AppShell.Aside-equivalente (panel lateral fijo). Multi-select por domain, scope, priority, tags. Estado local; rebuild request query.
|
||||
4. **Detail** (`TI_INFO " Detail"`) — modal/panel lateral con form: status (combo), priority (combo), scope (combo), tags (chips editables), depends/blocks (listas), body (read-only multiline).
|
||||
|
||||
## HTTP client (data.cpp)
|
||||
|
||||
- `fetch_issues(filters)` → GET con query string → parse JSON → vector<Issue>.
|
||||
- `fetch_flows()` → similar.
|
||||
- `patch_issue(id, partial)` → PATCH JSON → recibe issue actualizado.
|
||||
- `subscribe_sse()` thread aparte → push events a queue mutex → consumir en main loop → re-fetch afectados.
|
||||
|
||||
Usa `http_request_cpp_core` + `sse_client_cpp_core`. JSON via `nlohmann/json` (ya en cpp/vendor o sacar al header-only).
|
||||
|
||||
## DoD
|
||||
|
||||
- `cmake --build cpp/build/linux --target kanban_cpp -j` verde.
|
||||
- `./cpp/build/linux/apps/kanban_cpp/kanban_cpp --self-test` exit 0:
|
||||
- inicializa contexto ImGui sin display.
|
||||
- parsea respuesta JSON sintetica.
|
||||
- no toca red salvo si `--backend http://...` se pasa.
|
||||
- e2e_checks en `app.md`: build + self_test + backend_health (corre backend en background) + smoke (drag-drop reescribe MD).
|
||||
- Captura screenshot board con 4 columnas pobladas → guardar en `dod_evidence/board_screenshot.png`.
|
||||
|
||||
## Anti-scope
|
||||
|
||||
- Sin grafo de dependencias (epic 0130 lo describe como anti-scope v1).
|
||||
- Sin crear issues nuevos (solo editar existentes).
|
||||
- Sin edicion de body MD (solo frontmatter).
|
||||
- Sin syntax highlighting markdown.
|
||||
Reference in New Issue
Block a user