Seguimiento de planes mediante el kanban local
El kanban existe para que las sesiones de Claude, a través de diferentes proyectos y diferentes días, puedan mantener el estado del plan vivo entre sesiones: reclamar planes, moverlos entre estados, dejar notas. Varias instancias de Claude pueden compartir un mismo tablero — siempre agrupa tu trabajo bajo un slug de proyecto para que los planes de diferentes repos no colisionen.
Cuando estés trabajando en un plan no trivial en un proyecto en una máquina donde el kanban está corriendo, deberías estar hablando con él a medida que avanzas.
El tablero
Fuente: https://github.com/hechtch/kanban — una SPA Angular más una API Go sobre SQLite. Se ejecuta localmente; no hay una instancia alojada.
git clone https://github.com/hechtch/kanban.git
cd kanban
make container-build && make container-run # CONTAINER_RUNTIME=podman si usas podman
make container-run publica -p 8000:8000 y monta ~/.kanban/data para persistencia, por lo que un solo contenedor sirve tanto la interfaz de usuario como la API en el puerto 8000.
| Modo | UI para el humano | Base de la API del agente |
|---|---|---|
Contenedor (make container-run) | http://localhost:8000/board | http://localhost:8000/api/agent |
Desarrollo (make run) | http://localhost:4200/board | http://localhost:8000/api/agent |
La API del agente está en el puerto 8000 en ambos modos — solo el enlace de la UI del humano difiere, porque make run sirve el servidor de desarrollo de Angular por separado en el 4200. Usa el enlace de la UI del contenedor en el puerto 8000 a menos que sepas que el usuario está ejecutando make run.
Si el usuario lo tiene desplegado en otro lugar (detrás de un proxy inverso, un puerto publicado diferente), te dirá la URL base — toma la suya por encima de la predeterminada y conserva el sufijo /api/agent.
¿Está funcionando?
KB=http://localhost:8000/api/agent
curl -sS http://localhost:8000/api/health
# {"status":"ok"}
Si eso falla, el kanban no está corriendo. No lo inicies tú mismo — escribe en ~/.kanban/data/kanban.db y un segundo proceso compitiendo por esa base de datos es peor que no tener seguimiento. Dile al usuario que no está funcionando, ofrécele la línea make container-run de arriba, y continúa con el trabajo real sin seguimiento.
Especificación canónica — léela para cualquier detalle de endpoint
La API incluye una especificación OpenAPI 3.1 completa generada a partir de los manejadores tipados en Go. Es la fuente de verdad para rutas, formas de solicitud/respuesta, códigos de estado y reglas de validación — en caso de duda, obténla en lugar de adivinar o confiar en documentación desactualizada:
- Especificación (JSON):
http://localhost:8000/api/openapi.json - Especificación (YAML):
http://localhost:8000/api/openapi.yaml - Swagger UI (para humanos):
http://localhost:8000/api/docs
El resto de este archivo te proporciona el flujo de trabajo del agente (slugs, convenciones, recetas) para que no tengas que aprender la API desde cero. Para detalles específicos de endpoints no cubiertos aquí, lee la especificación.
Las respuestas incluyen un campo URL $schema que apunta al esquema JSON de la respuesta (convención de huma). ignóralo a menos que estés validando respuestas contra el esquema.
La convención de archivos de plan que asume esta habilidad
El tablero rastrea planes, y un plan es un archivo markdown en el repositorio:
plans/<topic>.md— un archivo por plan en curso discreto, describiendo el por qué y el qué. Usa casillas de verificación (- [ ]/- [x]) para tareas accionables y refínalas en el lugar a medida que avanza el trabajo.plans/done/<topic>.md— donde el archivo se mueve una vez que el trabajo se envía o se abandona, con un breve encabezado "Entregado en <versión> — " en la parte superior.CHANGELOG.md— el "qué cambió" orientado al público, emparejado conplans/done/que lleva el por qué.
Si un repositorio aún no usa esta estructura, crea plans/ y siembra el archivo antes de reclamarlo en el tablero — la regla del slug a continuación depende del nombre del archivo.
La regla del slug
Todo se identifica por slug, nunca por ID numérico. Nunca almacenes un task.id en tus indicaciones o código.
- Slug del plan = el nombre del archivo del plan menos
.md, en minúsculas.plans/photo-import.md→photo-import. - Slug del proyecto = el nombre del repositorio o iniciativa en kebab-case.
~/projects/photo-organizer→photo-organizer. (Para un proyecto llamado "Photo Organizer", el servidor deriva automáticamentephoto-organizerdel nombre; también puedes pasar un slug explícito). - Forma (aplicada en el servidor):
^[a-z0-9]([a-z0-9-]*[a-z0-9])?$. Los slugs incorrectos devuelven400. Minúsculas, guiones entre palabras, sin guiones bajos ni mayúsculas.
El cuerpo de la tarea ES el plan
Cuando reclames un plan por primera vez, envía el markdown del plan como el campo body. La base de datos del kanban es canónica a partir de ese momento — no sincronices las ediciones de vuelta al archivo durante el vuelo. El archivo plans/<slug>.md en disco es la semilla (usada una vez); plans/done/<slug>.md es la instantánea de archivo que escribes cuando el trabajo se envía.
Si vuelves a hacer PUT más tarde con un nuevo body, tu versión gana. Los humanos también pueden corregir errores tipográficos en la vista renderizada en /task/<task_id> en la interfaz de usuario; ambas ediciones terminan en la misma columna.
Reclamar un plan es lo que lo hace "de Claude"
No hay un campo de asignado que configurar. El tablero lo deriva: una tarea que lleva un plan_slug — que es exactamente lo que PUT /plans/<slug> crea — se renderiza bajo Claude en el filtro de asignados de la barra lateral y recibe un chip de Claude en la tarjeta. Las tareas que el humano hizo a mano no tienen plan_slug y permanecen bajo tú.
Por lo tanto, reclamar un plan a través de esta API es un acto visible. No reclames trabajo que el humano está haciendo por sí mismo solo para dejar una nota en él.
Estados
Cinco columnas del tablero, fijas:
todo → doing → blocked → awaiting_merge → done
Además de backlog (fuera del tablero, 6.º opcional).
blocked— el trabajo no puede continuar; explica por qué en una nota.awaiting_merge— el código está escrito y subido, esperando que un PR se fusione o una fusión manual. Diferente dedoneporque el trabajo aún no se ha enviado.- Cambia a
donesolo cuando la rama esté realmente fusionada en la línea principal. No hagas la transición automática.
'waiting' no es un estado válido — fue renombrado a 'blocked' en una migración.
Rama de git
Los planes (y cualquier tarea) pueden llevar un campo git_branch — la rama donde se está realizando el trabajo. Establécelo cuando crees una rama de funcionalidad para que el humano pueda verlo en la tarjeta:
curl -sS -X PUT $KB/plans/photo-import \
-H 'Content-Type: application/json' \
-d '{"git_branch":"feat/photo-import"}'
Limpia con {"git_branch": null}. La rama se renderiza como un chip monoespaciado en la tarjeta.
Modelo y esfuerzo sugeridos
Cada tarea puede llevar un model y un effort — una pista para cualquier agente que recoja el trabajo después. El humano los establece desde el ticket (o tú puedes, cuando conoces el peso de un plan), y se renderizan como un chip fable / xhigh en la tarjeta. Respétalos: cuando reanudes un plan cuya tarea dice sonnet / high, ese es el nivel que el propietario quiere que se gaste en él; un plan de auditoría fable / xhigh quiere el modelo grande con alto esfuerzo.
curl -sS -X PUT $KB/plans/security-audit \
-H 'Content-Type: application/json' \
-d '{"model":"fable","effort":"xhigh"}'
modeles una abreviatura de forma libre —fable,opus,sonnet,haikuson el conjunto habitual; el servidor convierte a minúsculas y recorta, y rechaza cualquier cosa de más de 40 caracteres.effortdebe ser uno delow,medium,high,xhigh,max(los niveles de esfuerzo de razonamiento de Claude Code); cualquier otra cosa es un422.- Ambos son opcionales e independientes; limpia con
null. Ausente significa "sin preferencia — usa el valor predeterminado de la sesión".
Proyectos: archivado y etiquetas predeterminadas
Un proyecto lleva dos campos además de nombre/color, ambos configurables en el mismo upsert PUT $KB/projects/<slug>:
-
archived— el proyecto está terminado. Sus planes desaparecen de la barra lateral y del tablero (plegados en una sección "archivados" que el humano puede expandir), pero no se elimina nada y todos los endpoints aún los devuelven. Establécelo cuando el trabajo de un proyecto se haya enviado:curl -sS -X PUT $KB/projects/photo-organizer \ -H 'Content-Type: application/json' -d '{"archived":true}' -
tags— etiqueta cada tarea en el proyecto que lleva. El servidor las fusiona en lostagsde cada tarea al leer, por lo que un plan bajo un proyecto etiquetado["infra"]regresa etiquetadoinfrasin haber sido etiquetado individualmente. Reemplaza todo el conjunto, como las etiquetas de tarea:curl -sS -X PUT $KB/projects/photo-organizer \ -H 'Content-Type: application/json' -d '{"tags":["media","infra"]}'Debido a que la fusión ocurre en la lectura, eliminar una etiqueta del proyecto la elimina de cada tarea a la vez. No establezcas la etiqueta de un proyecto en sus tareas individualmente — el servidor elimina las etiquetas heredadas del conjunto propio de una tarea, por lo que estarías escribiendo una no-operación.
Flujo de trabajo por sesión
KB=http://localhost:8000/api/agent
# 1. Asegúrate de que tu proyecto exista (idempotente — 201 la primera vez, 200 después).
curl -sS -X PUT $KB/projects/photo-organizer \
-H 'Content-Type: application/json' \
-d '{"name":"Photo Organizer","color":"#d4654a"}'
# 2. Reclama el plan + envía su cuerpo actual + ponlo bajo tu proyecto.
# El primer PUT crea con status=todo; PUTs posteriores parchean.
BODY=$(jq -Rs . < plans/photo-import.md)
curl -sS -X PUT $KB/plans/photo-import \
-H 'Content-Type: application/json' \
-d "{\"title\":\"Photo Import\",\"project_slug\":\"photo-organizer\",\"body\":$BODY}"
# 3. Crea una rama de funcionalidad e informa al kanban sobre ella.
git checkout -b feat/photo-import
curl -sS -X PUT $KB/plans/photo-import \
-H 'Content-Type: application/json' \
-d '{"git_branch":"feat/photo-import"}'
# 4. Cambia a haciendo con una nota. Los PUT de estado son idempotentes — el mismo estado
# sin una nota es una no-operación; con una nota, escribe una entrada de actividad.
curl -sS -X PUT $KB/plans/photo-import/status \
-H 'Content-Type: application/json' \
-d '{"status":"doing","note":"iniciando Fase 2"}'
# 5. Deja notas a medida que avanzas (más barato que un cambio de estado).
curl -sS -X POST $KB/plans/photo-import/notes \
-H 'Content-Type: application/json' \
-d '{"text":"fase 2 terminada; ejecutando pruebas"}'
# 6. Cuando el PR esté abierto, muévete a awaiting_merge.
curl -sS -X PUT $KB/plans/photo-import/status \
-H 'Content-Type: application/json' \
-d '{"status":"awaiting_merge","note":"PR #123 abierto"}'
# 7. Una vez fusionado, cambia a hecho y archiva el archivo.
curl -sS -X PUT $KB/plans/photo-import/status \
-H 'Content-Type: application/json' \
-d '{"status":"done","note":"fusionado en v0.3.0"}'
# luego `git mv plans/photo-import.md plans/done/photo-import.md`
Menciona el enlace de la UI del humano cuando acabes de crear o mover un plan — http://localhost:8000/board (o :4200 en modo de desarrollo).
Reglas y trampas
- El enumerado de estados es fijo:
todo,doing,blocked,awaiting_merge,done,backlog. No inventes nuevos. project_slugdesconocido devuelve422 unknown project_slug "X"— crea el proyecto antes de asignar. No hay creación automática silenciosa.project_slug: ""limpia el proyecto, equivalente aproject_id: null.- Upsert es un verdadero upsert —
PUT /plans/:slugcon{}es tu llamada "reclama si está ausente, parchea si está presente". Re-ejecutar no duplica; solo la primera llamada escribe una entrada de actividadcreate. - Un PUT de estado con el mismo estado es una no-operación a menos que pases una
note. Úsalo a tu favor — puedes reintentar de forma segura. - El registro de actividad es de solo añadir y es visible en
$KB/plans/<slug>/activity. Usa notas para dejar migas de pan ("Fase 2 terminada; iniciando Fase 3") en lugar de narrar en el cuerpo. - El cuerpo está en markdown renderizado en
/task/<task_id>en la UI. Usa markdown estándar: encabezados, listas (incluyendo- [ ]/- [x]), bloques de código, enlaces. Las tablas y citas también se renderizan.
Cuándo no molestarse
- Correcciones de errores puntuales, refactorizaciones, ajustes de documentación. El kanban rastrea planes — iniciativas de múltiples pasos que abarcan sesiones o llevan un contexto significativo. El trabajo de un solo commit no necesita un ticket.
- Si el repositorio del kanban es el proyecto en el que estás trabajando, documenta sus propias convenciones en su
CLAUDE.md— léelo en su lugar e ignora este archivo. - Si la verificación de salud falla, el tablero no está corriendo. No intentes iniciarlo; simplemente continúa e informa al usuario.