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:
@@ -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).
|
||||
Reference in New Issue
Block a user