- app.md Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
name, lang, domain, version, description, tags, uses_functions, uses_types, framework, entry_point, dir_path, service, e2e_checks
| name | lang | domain | version | description | tags | uses_functions | uses_types | framework | entry_point | dir_path | service | e2e_checks | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| kanban | go | tools | 0.5.2 | Kanban board con persistencia SQLite, drag-and-drop entre columnas (dnd-kit), tracking del tiempo por columna, adjuntos de archivos por card, notificaciones realtime (SSE) y modulos externos (Jira). Frontend Vite + React + Mantine v9 embebido en el binario Go. Endpoint MCP Streamable HTTP en /mcp. |
|
|
|
net/http + vite + react + mantine + dnd-kit | backend/main.go | apps/kanban |
|
|
Arquitectura
Single-binary: backend Go con frontend Vite embebido. SQLite local con tres tablas (columns, cards, card_column_history) y endpoints REST.
./kanban --port 8095 --db kanban.db
Schema SQLite (migrations/001_init.sql … 010_card_messages.sql)
- columns — id, name, position, created_at
- cards — id, title, description, column_id (FK), position, created_at, updated_at
- card_column_history — id, card_id (FK), column_id, entered_at, exited_at
- Una entrada con
exited_at IS NULL= posicion actual - Al mover una tarjeta a otra columna: cierra la entrada activa (
exited_at = now) e inserta una nueva - El borrado de tarjeta hace CASCADE sobre el historial
- Una entrada con
- card_messages (migration 010) — id, card_id (FK CASCADE), author_id (nullable), body, created_at. Comentarios humano-a-humano por card; distintos de
card_events(sistema) y/api/chat(LLM global).
API REST
| Metodo | Path | Cuerpo |
|---|---|---|
| GET | /api/board |
— (retorna {columns, cards}, cada card incluye time_in_column_ms) |
| POST | /api/columns |
{name} |
| PATCH | /api/columns/{id} |
{name?, position?} |
| DELETE | /api/columns/{id} |
— (cascade a cards) |
| POST | /api/columns/reorder |
{ids: [...]} |
| POST | /api/cards |
{column_id, title, description?} |
| PATCH | /api/cards/{id} |
{title?, description?} |
| DELETE | /api/cards/{id} |
— |
| POST | /api/cards/{id}/move |
{column_id, ordered_ids: [...]} |
| POST | /api/cards/{id}/duplicate |
— (clona la card en la misma columna al final; copia titulo+" (copia)", descripcion, color, requester, assignee, tags, stickers, deadline; NO copia historial ni mensajes) |
| GET | /api/cards/{id}/messages |
— (lista de comentarios humano-a-humano de la card) |
| POST | /api/cards/{id}/messages |
{body} (crea comentario; author = usuario de la sesion) |
| DELETE | /api/cards/{cid}/messages/{mid} |
— (solo el autor puede borrar su mensaje) |
| GET | /api/cards/{id}/history |
— (timeline con duraciones por columna) |
| GET | /api/flags |
— (retorna { <name>: bool } con los feature flags efectivos en esta instancia) |
| POST | /api/auth/register |
{username, password, display_name?} (devuelve 403 registration_disabled si el flag registration-enabled esta en false) |
Feature flags
dev/feature_flags.json (lado del repo) define los flags por instancia. Se cargan al arrancar (override con --flags <path>); fichero ausente equivale a "todos los flags en false". El endpoint GET /api/flags expone el estado actual para que el frontend oculte UI condicional (ej. el toggle de "Registrate" en LoginPage solo aparece cuando registration-enabled es true).
| Flag | Default | Efecto cuando esta en true |
|---|---|---|
registration-enabled |
false |
Permite crear cuentas nuevas via POST /api/auth/register y muestra el toggle "Registrate" en la pantalla de login. |
Frontend
- dnd-kit (
@dnd-kit/core+@dnd-kit/sortable) para drag-and-drop entre y dentro de columnas (multi-container). - Mantine v9 +
@tabler/icons-reactpara UI. - Modales con
@mantine/modals(confirmacion borrado, history timeline). - Time-in-column live:
time_in_column_msdel backend + tick local cada segundo para que el badge se actualice sin reload. - DnD con
closestCorners+DragOverlaypara feedback visual al arrastrar. - Auto-refresh: el board se recarga cada 30s (
api.getBoard) sin interaccion del usuario; equivalente a pulsar el boton de refresco. El tick de 1s del time-in-column es independiente y no toca red. - Modal de card en dos columnas (
CardEditPanel): izquierda mantieneCardForm(titulo, solicitante, descripcion, asignacion, tags); derecha es unTabsconChat(por defecto) |Enlaces|Archivos(proximamente). Tamaño del modal: 85% del viewport.- Chat per-card (
CardChatPanel): lista de comentarios humano-a-humano persistidos encard_messages. Enter envia, Shift+Enter salto de linea. Solo el autor puede borrar su propio mensaje. - Enlaces (
CardLinksPanel): extrae URLs (https?://...) de titulo, descripcion y cuerpo de cada mensaje del chat. Deduplica, muestra hostname + URL completa + badge de origen. Click abre en pestaña nueva (target="_blank").
- Chat per-card (
- Duplicar card: click derecho sobre la card abre el menu contextual (mismo que el boton
⋮), donde aparece el item "Duplicar". Al pulsarlo invocaPOST /api/cards/{id}/duplicate. La copia se inserta al final de la misma columna con titulo + " (copia)". - Sesion obligatoria para chat:
POST/DELETE /api/cards/{id}/messagesexige sesion activa (401 si falta).author_idsiempre poblado; no hay comentarios anonimos. - Archivos (
CardFilesPanel): adjuntos por card almacenados enapps/kanban/uploads/<card_id>/<random>__<safe_filename>(filesystem, gitignored). Tablacard_filescon soft-delete. Limite 10 MB por archivo. Tres vias de upload:- Drag&drop en el editor de descripcion (
CardForm) → inserta(imagen) o[name](url)(resto) en la posicion del cursor. - Drag&drop o boton paperclip en el chat (
CardChatPanel) → crea un mensaje cuyo cuerpo es la ref markdown. - Boton "Subir" en el tab Archivos → sube sin embed.
- El renderer de mensajes (
MessageBody) reconoce-><Image>thumb 220px y[name](url)-><Anchor>. Texto plano se renderiza conwhiteSpace: pre-wrap. - Endpoints:
POST /api/cards/{id}/files(multipart, 10 MB max),GET /api/cards/{id}/files,GET /api/files/{id}(sirve binario coninlineoattachmentsegun MIME),DELETE /api/files/{id}(soft delete).
- Drag&drop en el editor de descripcion (
Build
cd frontend && pnpm install && pnpm build
cd .. && CGO_ENABLED=1 go build -tags fts5 -o kanban .
./kanban --port 8095 --db kanban.db
# Browser: http://localhost:8095
Dev (frontend con HMR contra backend)
# Terminal 1
./kanban --port 8095 --db kanban.db
# Terminal 2
cd frontend && pnpm dev
# Browser: http://localhost:5180 (vite proxy /api → 8095)
Notas
- Puerto por defecto 8095.
- DB por defecto
kanban.dben cwd. - IDs de columnas y tarjetas: 16 chars hex (8 bytes random) via
random_hex_id_go_core. - El historial conserva la cronologia exacta — incluso despues de cerrar y reabrir el server, los tiempos vivos siguen contando desde
entered_at. - El borrado de columna hace CASCADE: las tarjetas se borran y su historial tambien. Si se quiere preservar el historial al borrar, deberia archivarse en lugar de borrar.
Capability growth log
Una linea por bump SemVer. Bump-type segun .claude/commands/version.md:
-
major: breaking observable (CLI args, schema BBDD propia, formato wire). -
minor: feature aditiva (nuevo panel, endpoint, opcion). -
patch: bugfix sin cambio observable. -
v0.1.0 (2026-05-18) — baseline.
-
v0.2.0 (2026-05-27) — adjuntos de archivos por card (issue 0128): tabla
card_filescon soft-delete, endpoints REST (POST/GET/DELETE /api/cards/{id}/files,GET/DELETE /api/files/{id}), tres vias de upload (drag&drop en descripcion y chat, boton en tab Archivos), render inline de imagenes viaMessageBody. Limite 10 MB. -
v0.3.1 (2026-05-21) — patch: debounce board.invalidated (300ms trailing) + autoClose 4s en toasts de notification.created. Fix de blow-up de memoria en navegador por ráfagas de SSE.
-
v0.4.0 (2026-05-22) — minor: endpoint MCP Streamable HTTP
/mcpcon per-user bearer tokens (tablamcp_tokens, migration 017). Modal "MCP tokens" en avatar menu para generar/listar/revocar. Vite proxy enruta/mcpa WSL. Usa nueva funcionmcp_server_http_go_infra. Doc endocs/MCP.md. -
v0.5.2 (2026-06-01) — patch: el alta a Jira rellena el campo obligatorio "Área Solicitante" (
customfield_10158) que el issue type Epic (y Mejora) del proyecto DATA exige en la pantalla de creacion. Sin esto, elcard.createddel 0.5.1 daba HTTP 400 "Solicitante is required". Nuevos campos enjiraConfig:requester_field,requester_map,requester_default.create()/update()inyectan el campo como single-select{value:<opcion>}resuelto desde el requester de la card (mapa case-insensitive) o el default. Como los requesters del kanban son nombres de persona (no departamentos), las cards caen al default (Transformación).seed-jira-datagana flags--requester-field/--requester-defaulty la rama de update ahora mergea config para no pisar ediciones de UI. -
v0.5.1 (2026-06-01) — patch:
handleCreateCardahora emite el eventocard.created(antes soloboard.invalidated, que no estaba en el filtro del modulo). Con esto la creacion de una card disparajiraHandler.createy sincroniza el alta a Jira, igual que ya ocurria con move (card.moved) y chat (message.created). El evento se emite tras aplicar assignee/tags para que el issue de Jira los lleve. -
v0.5.0 (2026-05-27) — minor: merge ramas notifications-realtime + modules con master post-files. Trae notificaciones SSE (tabla
notifications, migration 015), modulos externos para sincronizacion bidireccional (Jira, etc., tablamodules, migration 016), tokens MCP per-user (migration 017). Conserva files attachments del 0128. Renumeradas migrations notif 014/015/016 -> 015/016/017.