Files
egutierrez 466a055f72 chore: auto-commit (1 archivos)
- app.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 15:49:55 +02:00

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.
service
kanban
web
dnd-kit
mantine
sqlite
time-tracking
random_hex_id_go_core
parse_date_or_default_go_core
sqlite_open_go_infra
sqlite_apply_migrations_go_infra
sqlite_column_exists_go_infra
spa_handler_go_infra
http_router_go_infra
http_serve_go_infra
http_middleware_chain_go_infra
http_cors_middleware_go_infra
http_logger_middleware_go_infra
http_json_response_go_infra
http_error_response_go_infra
http_parse_body_go_infra
http_session_cookie_middleware_go_infra
http_session_token_extract_go_infra
http_session_cookie_set_go_infra
http_session_cookie_clear_go_infra
password_hash_go_infra
password_verify_go_infra
session_create_go_infra
session_cleanup_go_infra
duration_stats_go_datascience
format_duration_ts_core
format_datetime_short_ts_core
string_hash_palette_ts_core
color_bg_ts_ui
color_border_ts_ui
color_swatch_ts_ui
fetch_json_ts_infra
claude_stream_go_core
mcp_server_stdio_go_infra
mcp_server_http_go_infra
ws_upgrader_go_infra
DurationStats_go_datascience
net/http + vite + react + mantine + dnd-kit backend/main.go apps/kanban
port health_endpoint health_timeout_s systemd_unit systemd_scope restart_policy runtime pc_targets is_local_only
8095 /api/board 3 kanban.service user always systemd-user
aurgi-pc
false
id cmd timeout_s expect_exit
build_frontend cd frontend && pnpm install --frozen-lockfile && pnpm build 180 0
id cmd timeout_s expect_exit
build_backend CGO_ENABLED=1 go build -tags fts5 -o kanban . 120 0
id cmd timeout_s expect_exit
migrations_apply rm -f /tmp/kanban_e2e.db && ./kanban --port 0 --db /tmp/kanban_e2e.db --migrate-only 15 0
id cmd expect_stdout_contains
migrations_schema sqlite3 /tmp/kanban_e2e.db 'SELECT version FROM schema_migrations ORDER BY version;' 1
id cmd health timeout_s
smoke_api ./kanban --port 8195 --db /tmp/kanban_e2e.db & http://127.0.0.1:8195/api/board 10
id cmd timeout_s expect_exit
tests_go go test -tags fts5 -count=1 ./... 120 0
id cmd timeout_s expect_exit
smoke_files bash e2e/files_smoke.sh 30 0

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.sql010_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
  • 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-react para UI.
  • Modales con @mantine/modals (confirmacion borrado, history timeline).
  • Time-in-column live: time_in_column_ms del backend + tick local cada segundo para que el badge se actualice sin reload.
  • DnD con closestCorners + DragOverlay para 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 mantiene CardForm (titulo, solicitante, descripcion, asignacion, tags); derecha es un Tabs con Chat (por defecto) | Enlaces | Archivos (proximamente). Tamaño del modal: 85% del viewport.
    • Chat per-card (CardChatPanel): lista de comentarios humano-a-humano persistidos en card_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").
  • Duplicar card: click derecho sobre la card abre el menu contextual (mismo que el boton ), donde aparece el item "Duplicar". Al pulsarlo invoca POST /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}/messages exige sesion activa (401 si falta). author_id siempre poblado; no hay comentarios anonimos.
  • Archivos (CardFilesPanel): adjuntos por card almacenados en apps/kanban/uploads/<card_id>/<random>__<safe_filename> (filesystem, gitignored). Tabla card_files con soft-delete. Limite 10 MB por archivo. Tres vias de upload:
    1. Drag&drop en el editor de descripcion (CardForm) → inserta ![name](url) (imagen) o [name](url) (resto) en la posicion del cursor.
    2. Drag&drop o boton paperclip en el chat (CardChatPanel) → crea un mensaje cuyo cuerpo es la ref markdown.
    3. Boton "Subir" en el tab Archivos → sube sin embed.
    • El renderer de mensajes (MessageBody) reconoce ![alt](url) -> <Image> thumb 220px y [name](url) -> <Anchor>. Texto plano se renderiza con whiteSpace: pre-wrap.
    • Endpoints: POST /api/cards/{id}/files (multipart, 10 MB max), GET /api/cards/{id}/files, GET /api/files/{id} (sirve binario con inline o attachment segun MIME), DELETE /api/files/{id} (soft delete).

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.db en 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_files con 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 via MessageBody. 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 /mcp con per-user bearer tokens (tabla mcp_tokens, migration 017). Modal "MCP tokens" en avatar menu para generar/listar/revocar. Vite proxy enruta /mcp a WSL. Usa nueva funcion mcp_server_http_go_infra. Doc en docs/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, el card.created del 0.5.1 daba HTTP 400 "Solicitante is required". Nuevos campos en jiraConfig: 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-data gana flags --requester-field/--requester-default y la rama de update ahora mergea config para no pisar ediciones de UI.

  • v0.5.1 (2026-06-01) — patch: handleCreateCard ahora emite el evento card.created (antes solo board.invalidated, que no estaba en el filtro del modulo). Con esto la creacion de una card dispara jiraHandler.create y 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., tabla modules, migration 016), tokens MCP per-user (migration 017). Conserva files attachments del 0128. Renumeradas migrations notif 014/015/016 -> 015/016/017.