CHANGELOG.md
Rama main · réplica de lectura del repositorio original.
# Changelog
## 0.2.0 — 2026-09-25
This release comes from running the board daily with Claude Code agents for
four and a half months across several projects — hundreds of cards and
agent sessions, and one autonomous Claude-implements / Codex-reviews
pipeline. Almost every item below maps to something that went
wrong there.
### Fixed
- **Polling rules no longer fire every minute on the same card.** A
`task_idle` rule (e.g. "blocked for 7 days → priority high + comment")
re-applied itself on every tick for as long as the card stayed put; one
database collected ~750k identical history rows in four months, card
modals froze and `kanban_get` overflowed agent context. Polling rules now
act once per stay in a column (`rule_firings`), `set_priority` to the
current value is a no-op, and updates that change nothing write no history.
Clean up an affected database with `maintenance dedupe-history`.
- **Moves made over MCP trigger rules and webhooks.** `kanban_move` wrote
straight to SQLite, so the rule engine only ever saw drag-drops in the UI —
an agent approving a card never launched anything. The server now tails
`task_history` (event feed) and dispatches every change, whoever made it:
UI, REST, MCP agents, scripts importing the store, the rule engine itself.
- **A card created directly in a column counts as arriving there**
(`task_moved` with `from_status: null`). Cards an agent filed straight into
Approved used to wait forever.
- **`KANBAN_DB` is honoured** by the store (documented before, ignored in
code), and **`KANBAN_ACTOR` by the MCP server**.
- `POST /api/tasks` with malformed `links` returned 500 (`KeyError: 'type'`);
it is a 400 with a readable message now. Links/blockers on unknown tasks are
404s, unknown blocker ids and dependency cycles are rejected.
- Concurrent requests could hit `sqlite3.InterfaceError` on the shared
connection (`GET /api/board`); all store access is serialised now.
- Writes from two processes (the web server and an MCP server, or two MCP
servers) could fail instantly with "database is locked": transactions
started with a deferred `BEGIN`, read, then tried to write, and SQLite does
not wait in that case. Write transactions now start with `BEGIN IMMEDIATE`
and wait up to 15 s for the other writer.
- The external blocker could not be cleared from the UI (empty field was sent
as "leave unchanged"). `""` clears it in the UI, REST and MCP.
- Reordering a card inside its column reset its "in column since" time and
wrote a history row; drag-drop indexes now renumber the column instead of
producing duplicate positions.
- `run_command` processes were in the server's process group — restarting
the kanban (or launchd) killed running agents — and without `log_file`
their output went to `/dev/null`. They get their own session, a UTF-8
locale when launchd provides none, and a default log file.
- The HTTP MCP endpoint used fastapi-mcp's deprecated `mount()`, which served
SSE at `/mcp` although the docs said streamable HTTP.
- The Cursor setup in the docs pointed at the wrong config file.
### Added
- **Archive**: `archived_at` on tasks, `POST /api/tasks/{id}/archive`, an
`archive` rule action, an Archive button in the card and "· N archived" in
the top bar. Archived cards leave the board and keep their status.
(Archiving by `move_to cancelled` made done work look abandoned; run
`maintenance restore-archived` to fix old data.)
- **Assignee** over REST (`POST /api/tasks/{id}/assign`), MCP
(`kanban_assign`), rules (`assign` action) and editable in the card dialog.
- **Rule engine**: `blockers_done` trigger (with optional `resolved_statuses`),
`task_idle` in `hours`/`minutes`, rule-level `repeat_every` and
`wip_limit` (polling rules), `project_id` lists, a top-level `"paused": true` kill switch
(also `POST /api/automation/pause` and ⏸ in the top bar), a loop guard for
reactive rules, and invalid rules are skipped instead of disabling the file.
- **`run_command`**: one process per card and rule at a time,
`max_concurrent` with a queue, `max_runs` (block the card instead of the
N+1-th launch; a person moving it out of Blocked resets the count — rules
and agents don't), exit code 75 = "try again later", not counted. Queued
and running jobs are stored in the database: a server that stops leaves
working agents alone and starts nothing new; the next one re-attaches to
them (pid and process start time must match) before resuming the queue in
order. Jobs whose rule isn't loaded are kept and listed, not dropped. Running
and queued commands are listed in `/api/automation/status`, and cards show
`▶ agent` while one runs.
- **Agent launcher v2** (`examples/agent-launcher/launch-claude.sh`):
preflight (card still approved, `claude` found and logged in), per-card
lock, explicit `--mcp-config` + `--strict-mcp-config`, `--permission-mode
dontAsk`, hard timeout that kills the whole process tree (and cleans up
whatever the agent left running), optional model per card size, optional
gate command, and a card blocked with the reason when the agent leaves it
behind — only if nobody has moved it since. Usage limits are recognised
from the CLI's error output, not from any "429" in token counts. Covered
by tests with a fake `claude`.
- **MCP**: `kanban_assign` and `kanban_history` (16 tools); `kanban_get` returns
the newest 30 history rows plus `history_total`; mutating tools answer with a
short card and a `url`; `kanban_move(expected_from=…)` refuses to act on a
card someone else just moved; `KANBAN_PROJECT_ID` scopes list/search/board;
`KANBAN_MCP_HUMAN_ONLY` (default `done`) — agents stop at `testing`.
Arguments are strict: `project=`/`body=`/`column=` are errors that name the
right parameter (they used to be dropped silently, creating empty cards);
status names accept `"In progress"`, `"Testing"`, Russian column names.
- **`python -m kanban_mcp.connect <dir> --project <id>`** and
`POST /api/projects/{id}/connect`: write/merge `.mcp.json` and refresh the
CLAUDE.md block (and AGENTS.md). An existing entry keeps its name and your
settings; only the code path, database and project are updated. The
new-project wizard offers it.
- **`python -m kanban_store.maintenance`**: `doctor`, `dedupe-history`,
`restore-archived`, `vacuum` — dry-run by default, backup before `--apply`.
`dedupe-history` only removes identical automation rows less than
`--window-minutes` (5) apart with nothing else in between; `doctor` also
flags `.mcp.json` files whose `KANBAN_DB` points at another database.
- REST: `X-Kanban-Actor` header, `GET /api/tasks/{id}/history` (paged),
`history_limit` on `GET /api/tasks/{id}`, `expected_from` on moves (409),
`status`/`created_at`/`running` on every board card, `project_id` alias on
`/api/board`, `comment` alias for `text`, SSE at `/sse`.
- UI: card links `/t/T-042` and `/p/<project>/t/T-042` (click the id in the
dialog to copy), "time in column" chips, history in local time with a note
about older rows, critical priority and XL size.
- PLAN.md import: skips template placeholder lines, strips markdown from
titles, shortens titles over 120 characters (full text in the description),
refuses CLAUDE.md/AGENTS.md as plan sources; re-importing a file imported
by v0.1 recognises the old spellings instead of duplicating cards.
- CI: pytest on Python 3.12 and 3.13.
### Changed
- **Webhook and rule events come from the event feed**, ~1 s after the change
(immediately for changes made through this server). Moves by MCP agents and
scripts are now delivered too. Payloads include `actor`, and the task
carries its newest 20 history rows plus `history_total` instead of the full
history. Bulk writers (`KANBAN_EVENTS_QUIET_ACTORS`, default
`plan-import,maintenance`) trigger neither webhooks nor rules.
- The generated CLAUDE.md block is MCP-first: agents file new work with
`kanban_create`, and the block says plainly that PLAN.md is a one-shot
import (it used to promise a sync that did not exist).
- MCP `kanban_move`/`kanban_create`/`kanban_update`/`kanban_pull` answer
with a short card (pull: the full card with 10 history rows) instead of the
full card with its entire history.
- HTTP MCP: streamable HTTP at `/mcp`, SSE moved to `/sse`. HTTP MCP
clients are treated as agents: `KANBAN_MCP_HUMAN_ONLY` applies, changes are
recorded as `agent:mcp-http` (or the client's `X-Kanban-Actor`), and
host-side endpoints (file pickers, CLI login, writing into project folders),
creating/editing projects and the pause switch are not exposed as tools.
- REST request bodies reject unknown fields (422) instead of ignoring them;
`priority` accepts `critical/high/normal/low` (`medium` → `normal`),
`size` `S/M/L/XL`.
- Agents calling `kanban_move(…, "done")` get an error by default; set
`KANBAN_MCP_HUMAN_ONLY=""` to allow it.
- The MCP server no longer creates a database: if `KANBAN_DB` points at a
file that doesn't exist, every tool returns an error saying so (instead of
the agent working on a new, empty board).
- The launcher now stays in the foreground while the agent works (the kanban
tracks the process); scripts written for v0.1 that exit immediately still
work but lose de-duplication and `max_concurrent`.
- `launchd` template: fuller `PATH` and `LANG=en_US.UTF-8` for `run_command`.
### Upgrading from 0.1.x
1. **Before restarting a server that has `run_command` rules**, look at
`rules.json`: moves made over MCP (and cards created straight into a
column) now trigger rules too. If unsure, add `"paused": true` first and
un-pause when you've checked what will fire.
2. Pull, then `uv sync` (or `pip install -r requirements.txt`) and restart the
server. The schema migrates itself (v5: `tasks.archived_at`,
`rule_firings`, `command_runs`, `command_jobs`); v0.1 servers keep working
on a migrated database.
3. `KANBAN_DB` is honoured now (v0.1 silently used `<repo>/tasks.db`). Check
the value in every `.mcp.json` and in the launchd plist — `doctor` flags
`.mcp.json` entries that point at a different database.
4. `python -m kanban_store.maintenance doctor` — it reports history spam,
done cards relabelled as cancelled, archive rules that do that, projects
without `.mcp.json` and `.mcp.json` files pointing at another database.
Each finding names the fix command; the fixing commands are dry runs until
you add `--apply` and make a backup first. Run `restore-archived` only once
the server runs v0.2 (a v0.1 server doesn't know about archiving and its
old rule would relabel the cards again), and `vacuum` with the server stopped.
5. In `rules.json`, replace `"action": {"type": "move_to", "status":
"cancelled"}` archive rules with `{"type": "archive"}`, and consider
`max_concurrent`/`max_runs` on `run_command` rules.
6. HTTP MCP clients configured for SSE at `/mcp` → use `/sse`, or switch them
to streamable HTTP at `/mcp`.
7. Re-run `python -m kanban_mcp.connect <dir> --project <id>` in each project
to point it at this checkout and refresh the CLAUDE.md block.
## 0.1.2 — 2026-05-11
- uv quickstart: `pyproject.toml` + `uv.lock`, one command from clone to UI.
- HTTP MCP endpoint via fastapi-mcp for Cursor, new Cline and MCP Inspector.
- Integration docs for Cursor, Cline (HTTP) and MCP Inspector.
## 0.1.1 — 2026-05-09
- QUICKSTART, auto-launch agent pipeline example, automation status endpoint.
- Agent workflow rules in the generated CLAUDE.md block; `KANBAN_ACTOR` in
MCP examples; theme/profile screenshots.
## 0.1.0 — 2026-05-09
- Initial open-source release: multi-project board, REST + OpenAPI, stdio MCP
server, inbox watcher, PLAN.md import, automation rules, webhooks.