fc4180cbb3
- .claude/agents/fn-analizador/SKILL.md - .claude/agents/fn-constructor/SKILL.md - .claude/agents/fn-executor/SKILL.md - .claude/agents/fn-mejorador/SKILL.md - .claude/agents/fn-orquestador/SKILL.md - .claude/agents/fn-recopilador/SKILL.md - .claude/commands/app.md - .claude/commands/compile.md - .claude/commands/cpp-app.md - .claude/commands/create_functions.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.0 KiB
3.0 KiB
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
import "fn-registry/functions/infra"
const (
issuesDir = "$HOME/fn_registry/dev/issues"
flowsDir = "$HOME/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_mdson el par CRUD atómico. Siempre usarlos juntos.scan_issues_dirllama aparse_issue_mdinternamente — no reimplementar el walk.watch_dir_fsnotifyemite 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
writeque el watcher emite. El backend debe ignorar eventos generados por sus propios writes (comparar path + timestamp).