---
id: "0119"
title: "kanban_cpp: leer dev/issues + dev/flows como cards (sync layer)"
status: pendiente
type: feature
domain:
  - cpp-stack
  - kanban
  - dev-loop
scope: app
priority: alta
depends:
  - "0112"
blocks: []
related:
  - "0008"
  - "0109m"
  - "0114"
created: 2026-05-18
updated: 2026-05-18
tags: [kanban, issues, flows, sync, frontmatter]
flow: "0008"
---

# 0119 — kanban_cpp sync layer: issues + flows como cards

## Problema

kanban_cpp (0112) clona el backend de kanban_web con `operations.db` vacia. Pero la intencion del flow 0008 es **gestionar issues y flows directamente desde kanban_cpp**. Sin sync con `dev/issues/*.md` + `dev/flows/*.md`, el board nace vacio y el usuario tendria que recrear cards a mano.

## Decision

Anadir endpoint al backend de kanban_cpp que lee frontmatter de los `.md` y los expone como cards/columnas virtuales. Lectura directa de filesystem (no via `issues_api` 0109m — el API service aun no existe; cuando exista, swap).

### Mapping `.md` -> card

| Frontmatter | Campo card |
|---|---|
| `id` | `card.external_id` |
| `title` | `card.title` |
| `status` (`pendiente`/`en-curso`/`done`/`deferred`) | `column` (mapping) |
| `priority` (alta/media/baja) | `card.priority` |
| `type` (feature/bug/chore/app) | `card.tag` |
| `tags` | `card.tags` |
| `flow` | `card.flow_id` |
| `dod_evidence_schema` (0114) | `card.dod_items[]` |
| body `## Problema` + `## Decision` | `card.description` |

### Mapping status -> columna kanban

```
pendiente  -> Backlog
en-curso   -> Doing
en-revisión -> Review
done       -> Done
deferred   -> Deferred
```

### Tableros separados

- Board `issues`: source = `dev/issues/*.md`.
- Board `flows`: source = `dev/flows/*.md`, columnas distintas (Pending / Running / Done / Deferred). flow body trae `## Definition of Done (user-facing, 4 surfaces obligatorios)`.

### Endpoints nuevos backend kanban_cpp

- `GET /api/boards/issues/cards` — lee `dev/issues/*.md`, cachea 30s.
- `GET /api/boards/flows/cards` — lee `dev/flows/*.md`, cachea 30s.
- `PATCH /api/boards/<board>/cards/<id>` — escribe vuelta al `.md` (cambia `status` en frontmatter). Usa `edit_yaml_frontmatter_go_core` del registry (verificar existe; si no, crear via fn-constructor).
- `POST /api/boards/<board>/cards/<id>/launch` — proxy a `agent_runner_api:8486/api/runs`.

### Watcher de cambios

`fsnotify` sobre `dev/issues/` + `dev/flows/`. Cuando hay write -> invalida cache + push SSE a UI -> kanban_cpp refresca board.

## Criterios de aceptacion

- [ ] `apps/kanban_cpp/backend/issues_source.go` parsea frontmatter via funcion del registry.
- [ ] `apps/kanban_cpp/backend/flows_source.go` idem para flows.
- [ ] Endpoints arriba implementados con tests.
- [ ] `fsnotify` watcher activo; cambio en `.md` propaga a UI en < 2s.
- [ ] PATCH actualiza solo el campo `status` del frontmatter, preserva resto.
- [ ] Round-trip: PATCH `/cards/0113` status=`en-curso` -> file mtime cambia -> `dev/issues/0113-*.md` tiene `status: en-curso`.
- [ ] Click `Launch` en card invoca `agent_runner_api` con `issue_id` + DoD items extraidos del frontmatter.
- [ ] Tag taxonomy issue 0103 respetada — solo statuses canonicos.
- [ ] kanban_cpp UI muestra ambos boards (tabs `Issues` / `Flows`) con cards reales del repo al arrancar.
- [ ] e2e_check: crear `.md` dummy -> aparece en board en <5s.

## Gotchas

- Frontmatter mal formado (yaml invalid): card aparece con badge `parse-error` + tooltip detalle. NO crashea backend.
- Cambios concurrentes: humano edita `.md` con vim mientras agente lo PATCHea via API. Usar lock file `.md.lock` corto + retry.
- `fsnotify` no funciona bien en WSL para `/mnt/c/` paths. Backend corre en WSL, lee paths nativos (`$HOME/fn_registry/dev/...`). Verificar.
- Issue / flow grandes (>500 lineas body): NO mandar full body en `/cards` (lento). Devolver solo frontmatter + primeras 5 lineas. Body completo via `/cards/<id>` on demand.
- Cards sin `status` (frontmatter incompleto): default `pendiente`.
- Reordenamiento manual: drag-and-drop en UI cambia status (cruzar columna) PERO no orden vertical persistente — no hay campo `order` en frontmatter. Decision: orden por `updated` desc dentro de cada columna.

## Out of scope

- Sync bidireccional con `issues_api` (0109m) — cuando exista, swap filesystem por API call.
- Conflict resolution manual (3-way merge UI).
- Edicion inline del body markdown desde la card (solo PATCH de `status` + `priority` por ahora).
- Bulk operations (mover N cards a la vez).
