Repository navigation
Kotgent 0.7.0 — the work layer, a kanban board, and a browser test tier
The work layer
kotgent tracked sessions, not work. Ten parallel sessions across three repositories carried no answer
to "what is this one for", "what is left", or "which of these is waiting for me". This release adds the
layer that answers all three: an ordered, dependency-aware backlog per project, stored locally, that a
human grooms on a kanban board and an agent inside a session reads from and writes to.
A project is a .kotgent.json at the root of a working tree — committed, so the backlog travels with the
repository. Tasks rank by gaps rather than by index, so a move is one write and only a collapsed gap
rewrites a column. Dependencies are a graph: blocked is derived from it rather than stored, an edge that
would close a ring is refused, and closing a task re-stamps everything it frees so the board hears about
it. A session can be linked to a task in two writes that need no ordering, its row and terminal header
name the task, and finishing a session with Done closes the task it was working on.
kotgent task add | list | show | next | comment | review | done | move | dep | delete covers the same
surface from the terminal and answers in JSON, so an agent can take the next eligible task without a human
typing it.
A kanban board in the Web UI
Four columns over one selected project, with drag-and-drop, a task detail that floats over the board
rather than squeezing it, an activity feed, and dependency editing. The SPA gained a real router — /s/{id}
and /tasks/{ref} are addressable, Back and Forward work, and a deep link survives a reload. The command
palette reaches the board, a new task, and this session's task; the sidebar carries the app's navigation.
Task frames ride the existing events socket and fold into the same newest-rev-wins list the sessions use,
so the board follows the daemon without a second protocol.
Breaking: the token-gated API moved under /api/v1
Every gated route now lives under /api/v1. /hooks/* and /auth* deliberately did not move: each
adapter bakes its ingress URL into a per-session shell script on disk, so moving hooks would break every
already-running session.
There is no dual-mount grace period, and three things break rather than degrade:
- An older
kotgentbinary cannot talk to a newer daemon — every control call answers404. Upgrade
both together;brew upgrade kotgentand a daemon restart. - An already-open browser tab breaks hard. Its
/eventsupgrade falls to the static catch-all and
answers404instead of401, so the sign-out recovery — which keys on401— never fires. Reload the
page. - An installed service worker outlives its pages. With every tab closed it can wake on a push while
holding the old paths, fetch a404, and show a generic notification instead of a per-session one. This
one is silent and heals only when a navigation replaces the worker.
Four Web UI bugs, and how they were found
The Web UI was guarded by ten test files that asserted the text of served JavaScript and CSS — 8195
lines and 1181 contains( calls. That tier is retired. In its place there is a browser tier: 116
Playwright-for-Java tests driving real Chromium against a new scenario harness binary, with no npm
anywhere (Playwright's Node driver ships inside the Maven artifact).
It found four defects that text could not see, all fixed here:
- Preferences never closed after a save. Its guard against a concurrent edit compared the form's
revision against submit time, and the daemon's echo of our own write beats the PUT response over
loopback every time. - The board's card drag was effectively unclaimable. Pointer capture was taken after an 8 px slop,
which a pointer cannot survive on a 12×16 px handle. - The opt-in unicode addon fetched 65 KB and changed nothing.
term.unicodeis xterm's proposed API
and its getter throws withoutallowProposedApi, and the throw landed in the install's own.catch. - The board's status toast ate clicks across the bottom of the board, so a drop there landed nowhere.
The sharpest artefact of the migration: one retired test asserted that app.js contains the literal
if (sameForm) closeDialogFrom(submittedDialog) — the exact line that was the first bug. That tier had
not failed to catch the defect; it had pinned it as a contract, and it broke when the bug was fixed.