# kanban

> File-backed kanban board — tickets, threaded comments, agent assignment, the kanban:change and kanban:comment trigger types, and an injectable console board with agent profiles for planning and review.

| field | value |
|-------|-------|
| version | 0.1.21 |
| type | binary |
| license | Apache-2.0 |
| repo | https://github.com/iii-hq/workers |
| supported_targets | x86_64-apple-darwin, aarch64-apple-darwin, x86_64-pc-windows-msvc, aarch64-pc-windows-msvc, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, x86_64-unknown-linux-musl, armv7-unknown-linux-gnueabihf |
| author | iii |

## installation

```sh
iii trigger compose::add worker=kanban@0.1.21
```

## configuration

```yaml
- agents_path: agents
  columns:
    - id: backlog
      label: Backlog
    - id: todo
      label: To do
    - id: in_progress
      label: In progress
    - id: in_review
      label: In review
    - id: done
      label: Done
  data_path: data/kanban
  default_status: backlog
  id_prefix: KAN
  priorities:
    - low
    - medium
    - high
    - urgent
```

## dependencies

- `configuration` @ `latest`

## readme

# kanban

A file-backed kanban board for an iii project. Tickets, threaded comments,
an activity timeline, agent assignment, subtasks, releases and projects live
in one `board.json`; every mutation is a `kanban::*` function, and two trigger
types (`kanban:change`, `kanban:comment`) let agents wake on what happens to a
ticket instead of polling. The worker injects a board page, a ticket screen
and the Releases and Projects pages into the console, and ships five agent
profiles — Product Manager, Tech Lead, Backend Engineer, Frontend Engineer,
iii ADE Worker Designer — that plan, dispatch, build and review work as
tickets on this board.

## Table of contents

- [Install](#install)
- [Quickstart](#quickstart)
- [Configuration](#configuration)
- [Subtasks, releases and projects](#subtasks-releases-and-projects)
- [Custom trigger types](#custom-trigger-types)
- [Console pages](#console-pages)
- [Chat mentions](#chat-mentions)
- [Agent profiles](#agent-profiles)
- [Skills](#skills)
- [Development](#development)

## Install

```bash
iii trigger compose::add worker=kanban
```

`iii trigger compose::add` declares the worker in `worker-compose.yaml` and
starts it as part of the Compose project. The `configuration` worker (built
into the engine) stores the board settings; `iii-directory` is optional and
supplies the agent profiles the board can assign to.

## Quickstart

Create a ticket, claim it, comment, and hand it off. Every ticket has an
internal uuid and a human key (`KAN-1`); both work everywhere a ticket is
addressed, case-insensitively.

```bash
iii trigger kanban::ticket::create title="Search filters persist" priority=high
iii trigger kanban::ticket::update id=KAN-1 assignee=backend-engineer status=in_progress actor=backend-engineer
iii trigger kanban::comment::create ticket_id=KAN-1 body="Pushed the fix; ready for review." author=backend-engineer
iii trigger kanban::ticket::move id=KAN-1 status=in_review actor=backend-engineer
iii trigger kanban::ticket::get id=kan-1
```

Break a ticket into subtasks with `parent` (a key or uuid); the new ticket is
`KAN-2`, and `KAN-1` now reports `children` and a `progress` count:

```bash
iii trigger kanban::ticket::create parent=KAN-1 title="Persist filters in the URL" actor=backend-engineer
iii trigger kanban::ticket::get id=KAN-1
```

From a worker, the same calls go through the SDK:

```rust
use iii_sdk::protocol::TriggerRequest;
use iii_sdk::{InitOptions, register_worker};
use serde_json::json;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let iii = register_worker("ws://localhost:49134", InitOptions::default());

    let ticket = iii
        .trigger(TriggerRequest {
            function_id: "kanban::ticket::create".into(),
            payload: json!({ "title": "Search filters persist", "priority": "high" }),
            action: None,
            timeout_ms: Some(5_000),
        })
        .await?;
    println!("{}", ticket["key"]); // KAN-1

    let board = iii
        .trigger(TriggerRequest {
            function_id: "kanban::board::get".into(),
            payload: json!({}),
            action: None,
            timeout_ms: Some(5_000),
        })
        .await?;
    println!("{}", board["columns"]);
    Ok(())
}
```

`kanban::ticket::get` returns the ticket with its `comments` and `activity`
inline; `kanban::board::get` returns every configured column with its ticket
summaries (no description or thread). Both list reads take `updated_since`
and `compact`, and the board takes `statuses` and a per-column `limit`, so an
agent reads the active lanes in one small call and the `done` column only for
what changed:

```bash
iii trigger kanban::board::get --json '{"statuses":["todo","in_progress","in_review"],"compact":true}'
iii trigger kanban::ticket::list --json '{"status":"done","updated_since":"2026-09-10T00:00:00.000Z","compact":true}'
```

The writes (`kanban::ticket::create`, `kanban::ticket::update`,
`kanban::ticket::move`, `kanban::comment::create`) return the whole ticket,
comments and activity included. An agent that writes in a loop passes
`"compact": true` to get back only the compact row instead:

```bash
iii trigger kanban::ticket::move --json '{"id":"KAN-1","status":"in_review","actor":"backend-engineer","compact":true}'
```

`kanban::ticket::links` returns the tickets a ticket mentions and the
tickets that mention it, found by key (`KAN-12`) in titles, descriptions and
comments.

 Pass `actor` on writes and `author` on comments: the timeline
reads honestly, and the comment filters key on exactly that string. Deleting
a ticket (`kanban::ticket::delete`) is a soft delete — the row stays in the
board file with `deleted_at` set and `kanban::ticket::restore` undoes it —
and it is reserved for a person on the console's ticket screen; agents move
a finished ticket to `in_review` or `done` instead. Calls that change nothing
(deleting a deleted ticket, restoring a live one, moving a ticket to where it
already is, an update with no effect) record no activity and fire no trigger;
a deleted ticket cannot be moved (nor have its status changed by an update;
its other fields can still be edited). To change labels without overwriting a
concurrent edit, `kanban::ticket::update` takes `add_labels` and
`remove_labels` besides the whole-set `labels`.

The full function list is in
[`skills/SKILL.md`](skills/SKILL.md); request and response schemas come from
`engine::functions::info`.

## Configuration

Settings live in the `configuration` worker under the id `kanban` and are
edited from the console's Kanban settings form (columns, priorities, prefix,
paths) or by writing the YAML file the configuration worker persists. The
worker hot-reloads on `configuration:updated`; no restart is needed.

```yaml
data_path: data/kanban        # folder holding board.json; relative to III_COMPOSE_DIR (or the process cwd)
id_prefix: KAN                # human keys become KAN-1, KAN-2, …  (^[A-Z][A-Z0-9]*$)
default_status: backlog       # column a new ticket lands in when none is given
done_status: done             # the column that counts as done: progress, release status, ship; default = the last column
columns:                      # left to right; `id` is what tickets store, `label` is what the board shows
  - { id: backlog, label: Backlog }
  - { id: todo, label: To do }
  - { id: in_progress, label: In progress }
  - { id: in_review, label: In review }
  - { id: done, label: Done }
priorities: [low, medium, high, urgent]   # offered on a ticket, lowest first; the first one is the default
agents_path: agents           # fallback folder of <id>.md agent profiles when iii-directory is not installed
```

A stored value is repaired rather than refused: an unknown `default_status`
falls to the first column, an unknown `done_status` falls to the last column,
an invalid prefix falls back to `KAN`, duplicate column ids collapse, and an
empty list falls back to the default. Pass
`--config <file>.yaml` to seed the entry on first boot only; afterwards the
configuration worker is the source of truth. `kanban::config::info` prints
the resolved absolute paths and `done_status`; `kanban::board::get` returns
`done_status` too. The settings form picks it as the **Done column**.

`board.json` is now version 2. The first write that upgrades a version 1 file
keeps the original as `board.v1.json` next to it; going back to an older
kanban drops projects, releases and subtask links on its next write, so restore
that copy if you must downgrade.

## Subtasks, releases and projects

A ticket has at most one parent, one project and one release. The three are
independent: nothing is inherited, and progress counts only the `done_status`
column (In review is not done). `kanban::ticket::create` and
`kanban::ticket::update` take `parent` (a key or uuid), `project` and `release`
(an id or a name); a string sets, `null` clears, and leaving the field out
changes nothing. The ticket a write or `kanban::ticket::get` returns carries
its `parent`, its live `children` and `progress: { done, total }`; board and
list rows carry `parent_id`, `project_id` and `release_id`.

```bash
iii trigger kanban::ticket::update id=KAN-2 parent=KAN-1 release="Phase 2" project="iii cloud"
iii trigger kanban::ticket::update --json '{"id":"KAN-2","parent":null}'
iii trigger kanban::ticket::list --json '{"release_id":"","compact":true}'
```

**Subtasks.** A subtask is a ticket with a `parent`, and the hierarchy is one
level deep: the parent must exist, be live and have no parent of its own, a
ticket cannot be its own parent, and a ticket with live children cannot get
one. A child's status changes never touch the parent. `progress` is the
children in `done_status` over the live children (deleted ones are not
counted), so a parent moved to Done with open children shows a warning in the
console. Deleting a parent leaves its children; each then shows the parent as
deleted. A deleted ticket can't become a subtask (restore it first), and
restoring a subtask whose parent is gone, deleted or now a subtask itself
detaches it, with an activity line on both. Attaching or detaching writes an activity line on both tickets and
fires `kanban:change` for both. Creating a ticket with a `parent` does not copy
the parent's project or release; the console prefills them when you add a
subtask from the parent, and you can change them.

**Projects.** A project has a name, description, optional lead (an agent
profile or `user`) and color. It is `active` or `archived`; an archived
project refuses new references but keeps its tickets, and `restore` brings it
back. `kanban::project::list` returns each project with `total`, `done` and a
count per column.

**Releases.** A release has a name, description and one optional target date
(`YYYY-MM-DD`). Its status is derived, never set: `released` once `released_at`
is set, `active` when some live ticket in it is outside the first column,
`planned` otherwise. The board sorts releases by target date (undated last),
then by name with numbers in order (Phase 2 before Phase 10). A release does
not belong to a project; `kanban::release::list` breaks each one down by
project instead. Shipping closes the release: when tickets are not done,
`kanban::release::ship` fails with "N tickets are not done; choose where they go
(move_open_to)" until you pass `move_open_to`, either another release (which
must exist, differ from this one and not be released) or `null` to take them out
of any release. The response lists the `moved` tickets. A released release
still accepts tickets; a ticket in it that is not done counts as `reopened`.
`reopen` clears `released_at` (moved tickets stay where they went). A release
can be deleted only when no ticket, live or deleted, references it.

```bash
iii trigger kanban::release::create name="Phase 2" target_at=2026-10-30
iii trigger kanban::release::ship --json '{"id":"Phase 2","move_open_to":"Phase 3"}'
```

Names are unique per kind, ignoring case and surrounding spaces, and every
function that takes a project or release accepts its id or its name.

**Create from a label.** `kanban::label::convert` turns the tickets carrying a
label (case-insensitive) into a project or a release, either a new one (`name`)
or an existing one (`id`, or a `name` that already exists, which adds to it).
It reports the tickets by key in four buckets: `added`, `already_here`,
`in_other` (in another entity of the same kind; kept unless `replace` is set)
and `deleted` (skipped). `dry_run` computes the buckets without writing
anything. `remove_label` drops the label from the tickets that end up in the
entity, and for a release `mark_released_if_done` sets `released_at` when every
live ticket in it is done. Each changed ticket gets an activity line and a
`ticket.updated` event, and running it twice adds nothing.

```bash
iii trigger kanban::label::convert --json '{"label":"phase-2","into":"release","name":"Phase 2","dry_run":true}'
```

| Function | What it does | Agents |
|---|---|---|
| `kanban::project::list` | Projects with status, totals and column counts; `include_archived` adds archived ones. | allowed |
| `kanban::release::list` | Releases with derived status, totals, `reopened` and the per-project breakdown. | allowed |
| `kanban::project::create` / `update` / `archive` / `restore` | Manage a project. | denied |
| `kanban::release::create` / `update` / `ship` / `reopen` / `delete` | Manage a release. | denied |
| `kanban::label::convert` | Put the tickets with a label into a project or release. | denied |

`kanban::ticket::list` and `kanban::board::get` also filter by `parent_id`,
`project_id` and `release_id` (an empty string means none), and the board
returns `projects` and `releases` next to its columns. Agents read projects and
releases and can set `parent`, `project` and `release` on tickets; planning
them is a human's call in the console, which `iii-permissions.yaml` enforces.

## Custom trigger types

| Trigger type | Config | Fires when | Payload to subscribers |
|---|---|---|---|
| `kanban:change` | `{ ticket_id?, parent_id?, release_id?, events?, ticket?, metadata? }` | After every mutation. `ticket_id` (uuid or key) and `events` narrow it; `parent_id` fires for that ticket and any of its children; `release_id` fires for tickets in that release and for the change that moved one out of it; an empty config fires for everything. | `{ event, ticket_id, ticket_key, ticket, comment, activity, at }` — `event` is one of `ticket.created`, `ticket.updated`, `ticket.moved`, `ticket.deleted`, `ticket.restored`, `comment.created`. |
| `kanban:comment` | `{ ticket_id \| parent_id, author?, exclude_author?, root_only?, ticket? }` | A comment is created on that ticket, or with `parent_id` on that ticket or any of its children. One of the two is required; `author` keeps one author, `exclude_author` drops one (your own id), `root_only` ignores replies. | `{ event: "comment.created", ticket_id, ticket_key, ticket, comment, author, at }` |

`ticket` on either type is `full` (default: the ticket with comments and
activity inline) or `summary` (the board-card fields only); an agent that
re-reads the ticket on a wake should pass `summary` so each wake costs a card,
not a thread. `kanban:change` has no author filter, so a feedback loop that
must not wake on its own comments uses `kanban:comment` with `exclude_author`:

```json
engine::register_trigger {
  "trigger_type": "kanban:comment",
  "config": { "ticket_id": "KAN-7", "exclude_author": "backend-engineer", "ticket": "summary" },
  "once": false,
  "lifecycle": { "max_fires": 20, "expires_in_ms": 3600000 }
}
```

A feature ticket with subtasks needs one binding, not one per child:
`parent_id` on `kanban:comment` and on `kanban:change` covers the parent and
every child, including children attached later.

```json
engine::register_trigger {
  "trigger_type": "kanban:comment",
  "config": { "parent_id": "KAN-1", "exclude_author": "tech-lead", "ticket": "summary" },
  "once": false,
  "lifecycle": { "max_fires": 50, "expires_in_ms": 3600000 }
}
```

Bindings live in the worker's in-process map and die with the process;
`kanban::config::info` reports how many are held under `subscribers`. Pair a
standing wake with a `cron` check-in over `kanban::comment::list`, which
takes `ticket_id`, the author filters and `since` (no `parent_id`; list the
parent and each child).

## Console pages

While the worker is connected it injects four pages into the console:

- **Kanban** (page `kanban-board`) — the board: one lane per configured
  column, drag and drop between lanes, a new-ticket dialog, and a lane picker
  when the pane is narrow. A filter bar narrows it by key or title, labels
  (any or all of them), assignee, priority and last update, with a count per
  option; a dot marks tickets changed since your last visit. Filters can be
  saved as views (tabs above the bar), kept in this browser only. Project
  and Release filters list each option with its count and a "No project" /
  "No release" choice. **Group** splits the board into swimlanes by release,
  project or parent ticket (saved with the view); a subtask's card carries a
  plain "Subtask of KAN-57" line and a parent's card shows its progress, with
  a warning dot when it is Done with open subtasks. A **List**
  layout shows the same tickets grouped by status, sortable, with bulk
  status, priority, label, assignee and delete edits. Clicking a card opens
  the ticket beside the board; the card's **Open in this tab** action keeps
  it in the current pane instead (the console reuses any tab that already
  shows a ticket pane), and the inline view offers **Open in its own pane**
  to go the other way.
- **Ticket** (page `kanban-ticket`) — one ticket: status, priority,
  assignee (from `kanban::agent::list`), Project, Release, Parent and editable
  labels, a Markdown description with an editor, linked tickets (from
  `kanban::ticket::links`; ticket keys in the text become links), and the
  activity timeline with threaded comments, filterable to comments or changes.
  A parent has a **Subtasks** section with its progress, an Open / All switch,
  **Add existing ticket…** and a new-subtask action that starts in Backlog with
  the parent's project and release; a Done parent with open subtasks shows a
  warning with the ways out (move it back to In review, close the subtasks, or
  detach them). A subtask shows a parent strip above its title. Delete and
  restore live here, behind a confirmation.
- **Releases** (page `kanban-releases`) — every release with its derived
  status, target date and progress, sorted by target date. A release page lists
  its not-done and done tickets, the per-project breakdown and its details;
  **Mark as released…** asks where the not-done tickets go, **Reopen…**
  undoes it, and a release created from a label offers to add the tickets that
  carry the label but are not in it yet. **New release** and **Create from a
  label…** live here.
- **Projects** (page `kanban-projects`) — every project with its progress, lead,
  color, releases and most used labels; rename, **Archive…** (it confirms
  when tickets are open) and restore from here. **New project** and **Create
  from a label…** live here too. **Create from a label…** previews the
  buckets (will be added, already here, in another release or project, deleted)
  before anything is written, can run a batch for several labels at once, and
  keeps the label unless you ask to remove it.

The pages stay live over a `kanban:change` binding, and the Kanban
settings form edits the configuration above. Chat renders `kanban::*`
results as ticket cards and `kanban:*` trigger activity with the ticket and
comment it carried. Command palette rows: New ticket, Refresh board, Clear
filters, Refresh ticket, Edit description, Write a comment, Back to the board.

## Chat mentions

Tickets can be mentioned in a chat as `@kanban(id="<uuid>")`. In the
console composer, type `@kanban`, press Tab and search by key (`KAN-12`,
`12`) or title words; the ticket shows as a pill that previews on hover as
its ticket card (the one ticket function results show) and opens the ticket
screen on click.
Agents get the ticket's one-line summary with the message and read the whole
ticket through `kanban::ticket::get`.

Two internal functions provide this (see
[`crates/mention-contract`](../crates/mention-contract/README.md)):
`kanban::mention::search` (live tickets ranked by key, then title, then
description and labels) and `kanban::mention::get` (the view, including a
soft-deleted ticket marked as such), which declares the provider.

## Agent profiles

`agents/` ships five profiles. Each is a system prompt with preloaded
`skills` and `functions`; the harness freezes both into every session that
runs as that profile, and the board is the only channel between them.

| Profile id | Role | Preloaded skills |
|---|---|---|
| `product-manager` | Interviews the user, writes tickets whose descriptions carry observable acceptance criteria, and gates `in_review` → `done` (any caveat goes back to `in_progress`). | `kanban/tickets/feature-planning`, `kanban/tickets/acceptance-review`, `kanban/tickets/kanban-tickets` |
| `tech-lead` | Splits one feature on the seam between backend and frontend, dispatches each ticket with `harness::spawn`, stays reachable on comment wakes, verifies the seam. | `kanban/tickets/ticket-orchestration`, `kanban/tickets/kanban-tickets`, `kanban/tickets/acceptance-review` |
| `backend-engineer` | Builds workers, functions, triggers and configuration; verifies with a real call; reports on the ticket. | `kanban/iii-node/index`, `kanban/iii-node/configuration`, `kanban/tickets/ticket-worker` |
| `frontend-engineer` | Builds the browser app (Vite, React, TanStack, iii-browser-sdk); verifies in a real browser; reports with screenshots on the ticket. | `kanban/frontend/iii-browser-sdk`, `kanban/frontend/react`, `kanban/frontend/vite`, `kanban/frontend/tanstack-router`, `kanban/frontend/tanstack-query`, `kanban/frontend/web-accessibility`, `kanban/frontend/web-performance`, `kanban/frontend/frontend-testing`, `kanban/tickets/ticket-worker` |
| `ade-worker-designer` | Designs and builds the UI a worker injects into the ADE console — pages, renderers, configuration forms, scoped styles — against `@iii-dev/console-ui` and the iii Schematic design system; verifies in the running console. | `kanban/ade-worker-design/index`, `kanban/ade-worker-design/console-injectable-ui`, `kanban/ade-worker-design/patterns`, `kanban/ade-worker-design/console-design`, `kanban/iii-node/configuration`, `kanban/tickets/ticket-worker` |

All four extend the bundled `iii-minimal` identity and use their profile id
as `actor` / `author` / `exclude_author`, so the loop in
[`skills/tickets/kanban-tickets.md`](skills/tickets/kanban-tickets.md) closes.

### Where they land

- **Registry install.** `iii trigger compose::add worker=kanban` publishes
  the profiles into `<agents_folder>/<id>.md` and the skills under
  `<skills_folder>/kanban/…`, which is why the skill ids above are prefixed
  `kanban/`.
- **Local `path://` install.** Copy `agents/*.md` into the project's
  `agents/` folder and `skills/**` into `skills/kanban/` (keeping the same
  relative layout) so the ids the profiles preload resolve.

`directory::agents::list` shows them; `kanban::agent::list` is what the board
assigns from.

### Picking a model per profile

The profiles ship without a `model`, so each session takes the model of the
send (the console's model picker or `harness::send { options: { model } }`).
To pin one, add `model` and optionally `reasoning_effort` to the
frontmatter as `provider::model`. `router::models::list` prints the catalog
for the providers you installed (its ids read `provider/model`; the profile
spells the same key with `::`). The examples below use the `claude-code`
provider; with the API-key provider the same models are `anthropic::…`.

```yaml
---
name: Tech Lead
model: claude-code::claude-fable-5-1
reasoning_effort: high
extends: iii-minimal
---
```

`model` inherits through `extends`, so setting it on a shared base profile
pins every child. A useful split, from most to least demanding:

| Profile | What the work needs | Pick |
|---|---|---|
| `tech-lead` | Decomposition, contract design, judging a seam that "passes" but does not work; the costliest mistakes happen here. | The strongest model you run: `claude-code::claude-fable-5-1` (or `claude-code::claude-opus-5`) with `reasoning_effort: high`. |
| `product-manager` | Interviewing, writing precise acceptance criteria, refusing caveats at the gate. Long context, careful language, few calls. | `claude-code::claude-opus-5` at medium effort; `claude-code::claude-sonnet-5` is the economy choice. |
| `backend-engineer` | Many tool calls, real verification, Rust/Node code. Gains more from a strong coder than from deep deliberation. | `claude-code::claude-sonnet-5` (or an `openai-codex::…` model if Codex is your coding provider); raise to Opus for protocol or migration work. |
| `frontend-engineer` | Same shape as backend plus browser verification and screenshots; visual judgement matters. | `claude-code::claude-sonnet-5`; Opus when the ticket is a design pass. |
| `ade-worker-designer` | Long design-system and contract skills preloaded (about 40k tokens), visual judgement against screenshots, strict adherence to `index.d.ts`. | `claude-code::claude-opus-5`; Sonnet for small renderer or form tickets. |

Edit the file in `agents/` (or `directory::agents::update { id, … }`) and
the next session that runs as that profile picks it up; sessions already
running keep the model they started with.

## Skills

`skills/SKILL.md` is the worker overview agents read first. The rest is the
working doctrine the profiles preload, one folder per concern (each folder's
`index.md` maps its files):

- `tickets/` — how work moves through the board: `kanban-tickets` (the
  shared loop, the comment wake, subtasks, review vs done, never delete),
  `feature-planning` and `acceptance-review` (the Product Manager's halves),
  `ticket-orchestration` and `ticket-worker` (the dispatcher's and the
  dispatched engineer's halves; the feature ticket is the parent of the
  tickets it dispatches).
- `iii-node/` — building a Node worker (package shape, functions, triggers,
  asset delivery, compose wiring) with its bundled `configuration` reference.
- `ade-worker-design/` — the designer's half: the injectable UI contract
  (`console-injectable-ui`), the iii Schematic design system
  (`console-design`) and the record-shaped interaction recipes (`patterns`).
- `frontend/` — the Frontend Engineer's browser stack: `iii-browser-sdk`,
  `react`, `vite`, `tanstack-router`, `tanstack-query`, `web-accessibility`,
  `web-performance`, `frontend-testing`.

Fetch any of them with `directory::skills::get { "id": "kanban/<folder>/<name>" }`.

## Development

```bash
# UI (esbuild; react and @iii-dev/console-ui stay external)
pnpm --dir kanban/ui build
# worker
cd kanban
cargo fmt --all -- --check
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --all-features
```

`build.rs` runs the UI build when `ui/dist` is missing or stale; set
`SKIP_UI_BUILD=1` to embed the existing output. For the hot-reload loop run
`pnpm --dir kanban/ui watch` in one terminal and
`III_KANBAN_UI_WATCH=1 cargo run --bin kanban` in another; every open
console tab swaps the asset on rebuild. The worker connects to `III_URL`
(default `ws://127.0.0.1:49134`) and resolves `data_path` and `agents_path`
against `III_COMPOSE_DIR`.

## skills

This version ships 27 skill files for agents, also browsable on the worker page:

- [`SKILL.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=SKILL.md&version=0.1.21)
- [`agents/ade-worker-designer.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=agents%2Fade-worker-designer.md&version=0.1.21)
- [`agents/backend-engineer.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=agents%2Fbackend-engineer.md&version=0.1.21)
- [`agents/frontend-engineer.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=agents%2Ffrontend-engineer.md&version=0.1.21)
- [`agents/product-manager.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=agents%2Fproduct-manager.md&version=0.1.21)
- [`agents/tech-lead.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=agents%2Ftech-lead.md&version=0.1.21)
- [`skills/ade-worker-design/console-design.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Fade-worker-design%2Fconsole-design.md&version=0.1.21)
- [`skills/ade-worker-design/console-injectable-ui.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Fade-worker-design%2Fconsole-injectable-ui.md&version=0.1.21)
- [`skills/ade-worker-design/index.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Fade-worker-design%2Findex.md&version=0.1.21)
- [`skills/ade-worker-design/patterns.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Fade-worker-design%2Fpatterns.md&version=0.1.21)
- [`skills/frontend/frontend-testing.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Ffrontend-testing.md&version=0.1.21)
- [`skills/frontend/iii-browser-sdk.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Fiii-browser-sdk.md&version=0.1.21)
- [`skills/frontend/index.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Findex.md&version=0.1.21)
- [`skills/frontend/react.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Freact.md&version=0.1.21)
- [`skills/frontend/tanstack-query.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Ftanstack-query.md&version=0.1.21)
- [`skills/frontend/tanstack-router.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Ftanstack-router.md&version=0.1.21)
- [`skills/frontend/vite.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Fvite.md&version=0.1.21)
- [`skills/frontend/web-accessibility.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Fweb-accessibility.md&version=0.1.21)
- [`skills/frontend/web-performance.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ffrontend%2Fweb-performance.md&version=0.1.21)
- [`skills/iii-node/configuration.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Fiii-node%2Fconfiguration.md&version=0.1.21)
- [`skills/iii-node/index.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Fiii-node%2Findex.md&version=0.1.21)
- [`skills/tickets/acceptance-review.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ftickets%2Facceptance-review.md&version=0.1.21)
- [`skills/tickets/feature-planning.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ftickets%2Ffeature-planning.md&version=0.1.21)
- [`skills/tickets/index.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ftickets%2Findex.md&version=0.1.21)
- [`skills/tickets/kanban-tickets.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ftickets%2Fkanban-tickets.md&version=0.1.21)
- [`skills/tickets/ticket-orchestration.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ftickets%2Fticket-orchestration.md&version=0.1.21)
- [`skills/tickets/ticket-worker.md`](https://workers.iii.dev/workers/kanban?tab=skills&skill=skills%2Ftickets%2Fticket-worker.md&version=0.1.21)

### SKILL.md

# kanban

The `kanban` worker owns one board file (`<data_path>/board.json`) holding
every ticket with its comments and activity timeline inline. Every ticket
carries an internal uuid and a human key (`KAN-7`, case-insensitive); both
work everywhere a ticket is addressed. Columns, priorities, the key prefix and
the done column are configuration (`kanban::config::info` prints the live
values), and the default board is `backlog`, `todo`, `in_progress`,
`in_review`, `done`.

A ticket has at most one parent (a subtask), one project and one release,
independent of each other. A parent's `progress` counts only children in the
`done_status` column (the last column unless configured; `in_review` is not done).

Tickets are never removed by an agent: a finished ticket is moved to
`in_review` or `done`, and a human soft-deletes from the console's ticket
screen (the row stays on disk with `deleted_at` set). Writes take an `actor`
and comments an `author`; pass your own agent-profile id in both, because the
comment filters below key on exactly that string. The worker also injects a
board, a ticket, a releases and a projects page into the console.

## When to Use

- A feature is being planned, dispatched to other agent profiles, or reviewed,
  and the plan must outlive the chat session.
- You were dispatched onto a ticket and must claim it, report on it and hand
  it off where the reviewer will see it.
- You need to react to a human's or another agent's comment on one ticket
  without polling.
- The console should show a board or a ticket beside the conversation.

## Boundaries

- Not a general task queue or a durable job store; use `queue` for work that
  must be executed, and the board for work that must be planned and reviewed.
- No author filter on `kanban:change`; use `kanban:comment` with
  `exclude_author` for a feedback loop that must not wake on your own posts.
- Deleting and restoring tickets is the human's action; agents move tickets.
- Creating, shipping or archiving projects and releases, and converting a
  label into one, are console actions; agents list them and file tickets under them.
- Agent profiles are resolved through `iii-directory` (with the configured
  agents folder as the fallback); this worker does not create profiles.

## Functions

- `kanban::config::info` — resolved settings: board file path, columns, `done_status`, priorities, key prefix, agents folder.
- `kanban::board::get` — every column with its ticket summaries, plus `done_status`, `projects` and `releases` with their counts; `statuses`, `updated_since`, `limit` and `compact` keep the read small, and `parent_id`, `project_id` and `release_id` narrow it (`""` means none).
- `kanban::ticket::create` — create a ticket (title, description, status, priority, assignee, labels, actor); `parent`, `project` and `release` file it as a subtask or under a project or release.
- `kanban::ticket::get` — one ticket with comments and activity, by uuid or key, plus its `parent`, its `children` and a `progress` count.
- `kanban::ticket::list` — ticket summaries narrowed by column, assignee, `updated_since`, `parent_id`, `project_id` or `release_id`; `compact` rows for catch-up reads.
- `kanban::ticket::links` — the tickets one ticket mentions and the tickets that mention it, by key in title, description or comments; summary rows, by uuid or key.
- `kanban::ticket::update` — change title, description, status, priority, assignee, labels, `parent`, `project` or `release` (a string sets, `null` clears, absent leaves it); `add_labels` / `remove_labels` edit the current labels (ASCII case ignored, applied after `labels`, remove before add) so concurrent label edits do not overwrite each other. A patch that changes nothing records and fires nothing. A deleted ticket's other fields can be edited, but its status cannot be changed: restore it first.
- `kanban::ticket::move` — move a ticket to a column and position (the board drag and drop); moving to where it already is changes nothing, and a deleted ticket cannot be moved.
- `kanban::ticket::delete` / `kanban::ticket::restore` — soft delete and undo; human actions. Deleting a deleted ticket or restoring a live one changes nothing.
- Writes (`ticket::create` / `update` / `move`, `comment::create`) return the whole ticket; pass `compact: true` to get only its compact row (key, title, status, priority, assignee, comment_count) when you don't need the rest.
- `kanban::comment::create` — comment or reply (`parent_id`); replies stay one level deep.
- `kanban::comment::list` — comments filtered by author, excluded author, root only, or since a timestamp.
- `kanban::activity::list` — the ticket's timeline, oldest first, optionally narrowed by type or time.
- `kanban::agent::list` — assignable agent profiles and their ids.
- `kanban::project::list` — projects with status, totals and per-column counts; `include_archived` adds the archived ones.
- `kanban::release::list` — releases with derived status (planned, active, released), totals, counts and a per-project breakdown.
- `kanban::project::create` / `kanban::project::update` / `kanban::project::archive` / `kanban::project::restore` — manage projects; human actions.
- `kanban::release::create` / `kanban::release::update` / `kanban::release::ship` / `kanban::release::reopen` / `kanban::release::delete` — manage releases; `ship` needs `move_open_to` when tickets are not done; human actions.
- `kanban::label::convert` — turn the tickets carrying a label into a project or a release, with `dry_run` buckets; human action.

A subtask has one level: the parent must be live and have no parent, and a
ticket with live children cannot get one. Attaching or detaching writes an
activity line on both tickets. A subtask does not inherit its parent's project
or release; pass them. A project or release is addressed by id or name; an
archived project refuses new tickets, a released release still accepts them.

A user may mention a ticket in chat as `@kanban(id="<uuid>")`; the message
then arrives with a `<mentions>` block carrying the ticket's summary and the
pre-verified `kanban::ticket::get` call. To point the user at a ticket in a
reply, write `@kanban(id="<uuid>")` with the `id` a function returned — the
console renders it as a ticket pill. (`kanban::mention::search` and
`kanban::mention::get` serve the composer and that block; they are internal.)

## Reactive triggers

Register a `kanban:comment` trigger when a session or function should run
every time someone comments on one ticket — the reviewer's answer after a
hand-off, a human's reply, another agent's note — without re-reading the
board.

Reach for it when:

- You reported on a ticket and moved it to `in_review`, and the rejection or
  acceptance must reach you after your turn ended.
- You dispatched a ticket to another profile and its report arrives as a
  comment.

If you only need the comment you just wrote, use the return value of
`kanban::comment::create`; register a trigger only when a *different* author
should wake you. `kanban:change` is the board-wide sibling: it fires on every
event (`ticket.created`, `ticket.updated`, `ticket.moved`, `ticket.deleted`,
`ticket.restored`, `comment.created`) with the whole ticket, filtered by
ticket and event name only, and is the right primitive for keeping a view in
sync or for a done-watch on `ticket.moved`. Besides `ticket_id` and `events`
it takes `parent_id` (fires for the parent and for any of its children) and
`release_id` (fires for tickets in that release, and for the change that moves
one out of it).

### How to bind

1. Register a handler, or omit `function_id` from an agent session to be woken.
2. Register the trigger:

```json
engine::register_trigger {
  "trigger_type": "kanban:comment",
  "config": { "ticket_id": "KAN-7", "exclude_author": "<your profile id>", "ticket": "summary" },
  "once": false,
  "lifecycle": { "max_fires": 20, "expires_in_ms": 3600000 }
}
```

`ticket_id` or `parent_id` is required and matches the uuid or the key;
`parent_id` fires for comments on the parent or any of its children, so one
binding replaces one per child. `author`, `exclude_author` and `root_only`
narrow further. `"ticket": "summary"` on
either trigger type delivers the board-card fields instead of the whole
ticket with its thread — the right choice for an agent that re-reads the
ticket on a wake. Mutations fire triggers; reads do not. The worker delivers only to bindings in its own in-process map,
so pair a standing wake with a `cron` check-in over `kanban::comment::list`.
For the payload shape call `get function info` on the trigger type.

## api reference

```json
{
  "functions": [
    {
      "description": "Read a ticket's activity timeline: field changes, lane moves, deletion and comment events, oldest first.",
      "metadata": {},
      "name": "kanban::activity::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "limit": {
            "default": null,
            "format": "uint64",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "since": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "ticket_id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          },
          "types": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          }
        },
        "required": [
          "ticket_id"
        ],
        "title": "ActivityListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          }
        },
        "properties": {
          "activity": {
            "items": {
              "$ref": "#/definitions/Activity"
            },
            "type": "array"
          },
          "count": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "activity",
          "count"
        ],
        "title": "ActivityList",
        "type": "object"
      }
    },
    {
      "description": "List assignable agent profiles, resolved through iii-directory with a fallback to the configured agents folder.",
      "metadata": {},
      "name": "kanban::agent::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "Empty",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "AgentProfile": {
            "properties": {
              "color": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "icon": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "description": "Agent profile id used as the ticket assignee.",
                "type": "string"
              },
              "logo": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "model": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "name"
            ],
            "type": "object"
          }
        },
        "properties": {
          "agents": {
            "items": {
              "$ref": "#/definitions/AgentProfile"
            },
            "type": "array"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "description": "directory | agents-folder | unavailable",
            "type": "string"
          }
        },
        "required": [
          "agents",
          "source"
        ],
        "title": "AgentList",
        "type": "object"
      }
    },
    {
      "description": "Read the board: every configured column with its tickets, the done column, every project and release with its counts, plus the resolved paths and priorities. Narrow it with statuses, updated_since, limit and parent_id / project_id / release_id (empty string = none), and ask for compact rows to keep the response small.",
      "metadata": {},
      "name": "kanban::board::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "compact": {
            "default": false,
            "description": "Return the compact row (key, title, status, priority, assignee, comment_count) instead of the full summary.",
            "type": "boolean"
          },
          "include_deleted": {
            "default": false,
            "description": "Include soft-deleted tickets. Default false.",
            "type": "boolean"
          },
          "limit": {
            "default": null,
            "description": "At most this many tickets per column, from the top of the column.",
            "format": "uint64",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "parent_id": {
            "default": null,
            "description": "Only the subtasks of this ticket (uuid or key); empty string = tickets that are not subtasks.",
            "type": [
              "string",
              "null"
            ]
          },
          "project_id": {
            "default": null,
            "description": "Only tickets in this project (id or name); empty string = tickets in no project.",
            "type": [
              "string",
              "null"
            ]
          },
          "release_id": {
            "default": null,
            "description": "Only tickets in this release (id or name); empty string = tickets in no release.",
            "type": [
              "string",
              "null"
            ]
          },
          "statuses": {
            "default": null,
            "description": "Only these column ids (board order kept). Omit for every column.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "updated_since": {
            "default": null,
            "description": "Only tickets updated after this ISO timestamp — what changed since your last read.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "BoardGetInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "BoardColumnView": {
            "properties": {
              "id": {
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "tickets": {
                "items": {
                  "$ref": "#/definitions/TicketRow"
                },
                "type": "array"
              }
            },
            "required": [
              "id",
              "label",
              "tickets"
            ],
            "type": "object"
          },
          "ProjectStatus": {
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          },
          "ProjectView": {
            "description": "A named group of tickets (a product area), independent of releases.",
            "properties": {
              "archived_at": {
                "default": null,
                "description": "Set when archived; an archived project refuses new tickets.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "color": {
                "default": null,
                "description": "blue | purple | teal | green | amber | rose.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "counts": {
                "additionalProperties": {
                  "format": "uint64",
                  "minimum": 0,
                  "type": "integer"
                },
                "type": "object"
              },
              "created_at": {
                "type": "string"
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "done": {
                "description": "Of those, the ones in the done column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "lead": {
                "default": null,
                "description": "Agent profile id, or `user`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "description": "Unique among projects (trimmed, ASCII case ignored).",
                "type": "string"
              },
              "source_label": {
                "default": null,
                "description": "The label this project was converted from, if any.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "$ref": "#/definitions/ProjectStatus"
              },
              "total": {
                "description": "Live tickets in the project.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "counts",
              "created_at",
              "done",
              "id",
              "name",
              "status",
              "total",
              "updated_at"
            ],
            "type": "object"
          },
          "ReleaseProjectCount": {
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "project_id": {
                "description": "`null` for tickets in no project.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "ReleaseStatus": {
            "enum": [
              "planned",
              "active",
              "released"
            ],
            "type": "string"
          },
          "ReleaseView": {
            "description": "A deliverable that tickets are filed under. Its status is derived from the tickets; only `released_at` is stored.",
            "properties": {
              "counts": {
                "additionalProperties": {
                  "format": "uint64",
                  "minimum": 0,
                  "type": "integer"
                },
                "type": "object"
              },
              "created_at": {
                "type": "string"
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "name": {
                "description": "Unique among releases (trimmed, ASCII case ignored).",
                "type": "string"
              },
              "projects": {
                "description": "Per-project breakdown, biggest first; `project_id: null` is \"no project\".",
                "items": {
                  "$ref": "#/definitions/ReleaseProjectCount"
                },
                "type": "array"
              },
              "released_at": {
                "default": null,
                "type": [
                  "string",
                  "null"
                ]
              },
              "reopened": {
                "description": "Not-done live tickets of a released release (0 while it is not released).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source_label": {
                "default": null,
                "description": "The label this release was converted from, if any.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "allOf": [
                  {
                    "$ref": "#/definitions/ReleaseStatus"
                  }
                ],
                "description": "released: `released_at` is set. active: some live ticket is outside the first column. planned: otherwise."
              },
              "target_at": {
                "default": null,
                "description": "`YYYY-MM-DD`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "counts",
              "created_at",
              "done",
              "id",
              "name",
              "projects",
              "reopened",
              "status",
              "total",
              "updated_at"
            ],
            "type": "object"
          },
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketRow": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketSummary"
              },
              {
                "$ref": "#/definitions/TicketBrief"
              }
            ],
            "description": "One list row: the summary by default, the brief when `compact` was asked."
          },
          "TicketSummary": {
            "description": "The board-card projection of a ticket.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "type": "string"
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "labels": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "created_at",
              "id",
              "key",
              "labels",
              "order",
              "priority",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "board_file": {
            "type": "string"
          },
          "columns": {
            "description": "The columns asked for (all by default), each with the tickets the filters kept.",
            "items": {
              "$ref": "#/definitions/BoardColumnView"
            },
            "type": "array"
          },
          "data_path_resolved": {
            "type": "string"
          },
          "default_status": {
            "type": "string"
          },
          "deleted_count": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "done_status": {
            "description": "The column that counts as done for progress, releases and subtasks.",
            "type": "string"
          },
          "id_prefix": {
            "type": "string"
          },
          "priorities": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "projects": {
            "description": "Every project, archived ones included, by name.",
            "items": {
              "$ref": "#/definitions/ProjectView"
            },
            "type": "array"
          },
          "releases": {
            "description": "Every release, by target date (none last) then name.",
            "items": {
              "$ref": "#/definitions/ReleaseView"
            },
            "type": "array"
          },
          "ticket_count": {
            "description": "Every live ticket on the board, regardless of the filters above.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "board_file",
          "columns",
          "data_path_resolved",
          "default_status",
          "deleted_count",
          "done_status",
          "id_prefix",
          "priorities",
          "projects",
          "releases",
          "ticket_count",
          "updated_at"
        ],
        "title": "BoardView",
        "type": "object"
      }
    },
    {
      "description": "Comment on a ticket, or reply to a comment by passing parent_id. Returns the new comment and the ticket (only its compact row with compact).",
      "metadata": {},
      "name": "kanban::comment::create",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "author": {
            "default": null,
            "description": "Agent profile id, or `user`. Defaults to `user`.",
            "type": [
              "string",
              "null"
            ]
          },
          "body": {
            "description": "Comment body. Markdown is rendered in the console.",
            "type": "string"
          },
          "compact": {
            "default": false,
            "description": "Return the ticket as its compact row (key, title, status, priority, assignee, comment_count) instead of the full ticket.",
            "type": "boolean"
          },
          "parent_id": {
            "default": null,
            "description": "Comment id to reply to; replies stay one level deep.",
            "type": [
              "string",
              "null"
            ]
          },
          "ticket_id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          }
        },
        "required": [
          "body",
          "ticket_id"
        ],
        "title": "CommentCreateInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketView": {
            "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
            "properties": {
              "activity": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Activity"
                },
                "type": "array"
              },
              "assignee": {
                "default": null,
                "description": "Agent profile id.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "children": {
                "description": "Live subtasks by ticket number.",
                "items": {
                  "$ref": "#/definitions/Brief"
                },
                "type": "array"
              },
              "comments": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Comment"
                },
                "type": "array"
              },
              "created_at": {
                "type": "string"
              },
              "created_by": {
                "default": "user",
                "type": "string"
              },
              "deleted_at": {
                "default": null,
                "description": "Set when soft-deleted; the row stays on disk.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "id": {
                "description": "Internal uuid.",
                "type": "string"
              },
              "key": {
                "description": "Human-readable id, e.g. KAN-1.",
                "type": "string"
              },
              "labels": {
                "default": [],
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "default": 0,
                "description": "Position inside its column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Brief"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
              },
              "parent_id": {
                "default": null,
                "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "progress": {
                "$ref": "#/definitions/Progress"
              },
              "project_id": {
                "default": null,
                "description": "Uuid of the project (several tickets, independent of the release).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "default": null,
                "description": "Uuid of the release.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Column id.",
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "children",
              "created_at",
              "id",
              "key",
              "priority",
              "progress",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          },
          "TicketWritten": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketView"
              },
              {
                "$ref": "#/definitions/TicketBrief"
              }
            ],
            "description": "What a ticket write returns: the full ticket, or its compact row when the request asked for `compact`."
          }
        },
        "properties": {
          "comment": {
            "$ref": "#/definitions/Comment"
          },
          "ticket": {
            "$ref": "#/definitions/TicketWritten"
          }
        },
        "required": [
          "comment",
          "ticket"
        ],
        "title": "CommentCreated",
        "type": "object"
      }
    },
    {
      "description": "Read a ticket's comments with filtering, mirroring the kanban:comment trigger: by author, excluding an author, roots only, or after a timestamp.",
      "metadata": {},
      "name": "kanban::comment::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "author": {
            "default": null,
            "description": "Only comments by exactly this author.",
            "type": [
              "string",
              "null"
            ]
          },
          "exclude_author": {
            "default": null,
            "description": "Skip comments by this author — e.g. your own.",
            "type": [
              "string",
              "null"
            ]
          },
          "include_deleted": {
            "default": false,
            "type": "boolean"
          },
          "limit": {
            "default": null,
            "format": "uint64",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "parent_id": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "root_only": {
            "default": false,
            "type": "boolean"
          },
          "since": {
            "default": null,
            "description": "ISO timestamp; only comments created after it.",
            "type": [
              "string",
              "null"
            ]
          },
          "ticket_id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          }
        },
        "required": [
          "ticket_id"
        ],
        "title": "CommentListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          }
        },
        "properties": {
          "comments": {
            "items": {
              "$ref": "#/definitions/Comment"
            },
            "type": "array"
          },
          "count": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          }
        },
        "required": [
          "comments",
          "count"
        ],
        "title": "CommentList",
        "type": "object"
      }
    },
    {
      "description": "Resolved kanban settings: the folder the board file lives in, the columns, and the numbering prefix.",
      "metadata": {},
      "name": "kanban::config::info",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "Empty",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Column": {
            "properties": {
              "id": {
                "description": "Stable column id stored on tickets.",
                "type": "string"
              },
              "label": {
                "description": "Name shown on the board.",
                "type": "string"
              }
            },
            "required": [
              "id",
              "label"
            ],
            "type": "object"
          },
          "SubscriberCounts": {
            "properties": {
              "change": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              },
              "comment": {
                "format": "uint",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "change",
              "comment"
            ],
            "type": "object"
          }
        },
        "properties": {
          "agents_path": {
            "type": "string"
          },
          "agents_path_resolved": {
            "type": "string"
          },
          "board_file": {
            "description": "Absolute path of board.json.",
            "type": "string"
          },
          "columns": {
            "description": "Board columns left to right.",
            "items": {
              "$ref": "#/definitions/Column"
            },
            "type": "array"
          },
          "configuration_id": {
            "type": "string"
          },
          "data_path": {
            "description": "Configured path, as written.",
            "type": "string"
          },
          "data_path_resolved": {
            "description": "Absolute folder the board file is read from.",
            "type": "string"
          },
          "default_status": {
            "type": "string"
          },
          "done_status": {
            "description": "The column that counts as done for progress, releases and subtasks.",
            "type": "string"
          },
          "id_prefix": {
            "type": "string"
          },
          "priorities": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "project_root": {
            "type": "string"
          },
          "subscribers": {
            "$ref": "#/definitions/SubscriberCounts"
          }
        },
        "required": [
          "agents_path",
          "agents_path_resolved",
          "board_file",
          "columns",
          "configuration_id",
          "data_path",
          "data_path_resolved",
          "default_status",
          "done_status",
          "id_prefix",
          "priorities",
          "project_root",
          "subscribers"
        ],
        "title": "ConfigInfo",
        "type": "object"
      }
    },
    {
      "description": "Turn a label into a project or a release: create one (or reuse the one with that name, or pass an existing id) and file every ticket carrying the label under it. Reports which tickets were added, already there, in another project/release (kept unless replace) or deleted (skipped). Running it twice adds nothing; dry_run changes nothing.",
      "metadata": {},
      "name": "kanban::label::convert",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ConvertInto": {
            "enum": [
              "project",
              "release"
            ],
            "type": "string"
          }
        },
        "properties": {
          "description": {
            "default": null,
            "description": "Description of a project or release created by this call.",
            "type": [
              "string",
              "null"
            ]
          },
          "dry_run": {
            "default": false,
            "description": "Compute the buckets and change nothing.",
            "type": "boolean"
          },
          "id": {
            "default": null,
            "description": "An existing project or release (id or name) to add the tickets to.",
            "type": [
              "string",
              "null"
            ]
          },
          "into": {
            "allOf": [
              {
                "$ref": "#/definitions/ConvertInto"
              }
            ],
            "description": "Convert into a project or a release."
          },
          "label": {
            "description": "The label to convert; matched ASCII case-insensitively.",
            "type": "string"
          },
          "mark_released_if_done": {
            "default": false,
            "description": "Releases: mark the release released when every live ticket in it is done, dated at the last of them.",
            "type": "boolean"
          },
          "name": {
            "default": null,
            "description": "Without `id`: the name of the project or release to create, or to reuse when one has this name.",
            "type": [
              "string",
              "null"
            ]
          },
          "remove_label": {
            "default": false,
            "description": "Remove the label from the tickets that end up in the project or release. Default false.",
            "type": "boolean"
          },
          "replace": {
            "default": false,
            "description": "Also move tickets that are in another project or release. Default false: they are kept where they are.",
            "type": "boolean"
          },
          "target_at": {
            "default": null,
            "description": "Target date `YYYY-MM-DD` of a release created by this call.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "into",
          "label"
        ],
        "title": "LabelConvertInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Buckets": {
            "description": "Ticket keys per outcome. A bucket describes where a ticket was before the conversion; with `replace`, `in_other` tickets are moved all the same.",
            "properties": {
              "added": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "already_here": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "deleted": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "in_other": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              }
            },
            "required": [
              "added",
              "already_here",
              "deleted",
              "in_other"
            ],
            "type": "object"
          },
          "EntityView": {
            "anyOf": [
              {
                "$ref": "#/definitions/ProjectView"
              },
              {
                "$ref": "#/definitions/ReleaseView"
              }
            ],
            "description": "A project or a release, as `kanban::label::convert` returns it."
          },
          "ProjectStatus": {
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          },
          "ProjectView": {
            "description": "A named group of tickets (a product area), independent of releases.",
            "properties": {
              "archived_at": {
                "default": null,
                "description": "Set when archived; an archived project refuses new tickets.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "color": {
                "default": null,
                "description": "blue | purple | teal | green | amber | rose.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "counts": {
                "additionalProperties": {
                  "format": "uint64",
                  "minimum": 0,
                  "type": "integer"
                },
                "type": "object"
              },
              "created_at": {
                "type": "string"
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "done": {
                "description": "Of those, the ones in the done column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "lead": {
                "default": null,
                "description": "Agent profile id, or `user`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "description": "Unique among projects (trimmed, ASCII case ignored).",
                "type": "string"
              },
              "source_label": {
                "default": null,
                "description": "The label this project was converted from, if any.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "$ref": "#/definitions/ProjectStatus"
              },
              "total": {
                "description": "Live tickets in the project.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "counts",
              "created_at",
              "done",
              "id",
              "name",
              "status",
              "total",
              "updated_at"
            ],
            "type": "object"
          },
          "ReleaseProjectCount": {
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "project_id": {
                "description": "`null` for tickets in no project.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "ReleaseStatus": {
            "enum": [
              "planned",
              "active",
              "released"
            ],
            "type": "string"
          },
          "ReleaseView": {
            "description": "A deliverable that tickets are filed under. Its status is derived from the tickets; only `released_at` is stored.",
            "properties": {
              "counts": {
                "additionalProperties": {
                  "format": "uint64",
                  "minimum": 0,
                  "type": "integer"
                },
                "type": "object"
              },
              "created_at": {
                "type": "string"
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "name": {
                "description": "Unique among releases (trimmed, ASCII case ignored).",
                "type": "string"
              },
              "projects": {
                "description": "Per-project breakdown, biggest first; `project_id: null` is \"no project\".",
                "items": {
                  "$ref": "#/definitions/ReleaseProjectCount"
                },
                "type": "array"
              },
              "released_at": {
                "default": null,
                "type": [
                  "string",
                  "null"
                ]
              },
              "reopened": {
                "description": "Not-done live tickets of a released release (0 while it is not released).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source_label": {
                "default": null,
                "description": "The label this release was converted from, if any.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "allOf": [
                  {
                    "$ref": "#/definitions/ReleaseStatus"
                  }
                ],
                "description": "released: `released_at` is set. active: some live ticket is outside the first column. planned: otherwise."
              },
              "target_at": {
                "default": null,
                "description": "`YYYY-MM-DD`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "counts",
              "created_at",
              "done",
              "id",
              "name",
              "projects",
              "reopened",
              "status",
              "total",
              "updated_at"
            ],
            "type": "object"
          },
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketRow": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketSummary"
              },
              {
                "$ref": "#/definitions/TicketBrief"
              }
            ],
            "description": "One list row: the summary by default, the brief when `compact` was asked."
          },
          "TicketSummary": {
            "description": "The board-card projection of a ticket.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "type": "string"
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "labels": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "created_at",
              "id",
              "key",
              "labels",
              "order",
              "priority",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "buckets": {
            "allOf": [
              {
                "$ref": "#/definitions/Buckets"
              }
            ],
            "description": "Ticket keys per outcome, from where the tickets were before the call."
          },
          "created": {
            "type": "boolean"
          },
          "entity": {
            "anyOf": [
              {
                "$ref": "#/definitions/EntityView"
              },
              {
                "type": "null"
              }
            ],
            "description": "The project or release after the conversion; with `dry_run`, the existing one or null."
          },
          "tickets": {
            "description": "The tickets that were changed.",
            "items": {
              "$ref": "#/definitions/TicketRow"
            },
            "type": "array"
          }
        },
        "required": [
          "buckets",
          "created",
          "tickets"
        ],
        "title": "ConvertResult",
        "type": "object"
      }
    },
    {
      "description": "Resolve a @kanban(id=…) chat mention to its pill, preview card and agent summary; null for an unknown id. Declares the kanban mention provider.",
      "metadata": {
        "internal": true,
        "mention": {
          "color": "blue",
          "description": "A kanban ticket, by uuid or human key (KAN-12)",
          "details": {
            "function_id": "kanban::ticket::get",
            "id_field": "id"
          },
          "icon": "ticket",
          "label": "Tickets",
          "name": "kanban",
          "search": "kanban::mention::search",
          "v": 1
        },
        "trace_hidden": true
      },
      "name": "kanban::mention::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Input of a provider's get function.",
        "properties": {
          "id": {
            "description": "The id from the token.",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "MentionGetRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "anyOf": [
          {
            "$ref": "#/definitions/MentionView"
          },
          {
            "type": "null"
          }
        ],
        "definitions": {
          "MentionField": {
            "description": "One labelled value of a preview card.",
            "properties": {
              "label": {
                "type": "string"
              },
              "tone": {
                "description": "`neutral`, `info`, `success`, `warning` or `danger`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "value": {
                "type": "string"
              }
            },
            "required": [
              "label",
              "value"
            ],
            "type": "object"
          },
          "MentionOpen": {
            "anyOf": [
              {
                "properties": {
                  "context": true,
                  "page": {
                    "description": "Console page id (as registered with `host.pages`).",
                    "type": "string"
                  }
                },
                "required": [
                  "page"
                ],
                "type": "object"
              },
              {
                "properties": {
                  "session": {
                    "description": "Session id to select.",
                    "type": "string"
                  }
                },
                "required": [
                  "session"
                ],
                "type": "object"
              },
              {
                "properties": {
                  "url": {
                    "type": "string"
                  }
                },
                "required": [
                  "url"
                ],
                "type": "object"
              }
            ],
            "description": "What clicking a mention opens: an injected console page, a chat session, or an http(s) URL."
          },
          "MentionView": {
            "description": "What a chat surface renders for one mention, and what an agent reads. A get function answers `null` for an id it does not know.",
            "properties": {
              "color": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "data": {
                "description": "The domain object, for a worker's own preview renderer."
              },
              "description": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "fields": {
                "description": "Labelled values for the preview card (status, assignee, …).",
                "items": {
                  "$ref": "#/definitions/MentionField"
                },
                "type": "array"
              },
              "hint": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "icon": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "description": "The canonical id (may differ from the one asked for, e.g. a key resolved to a uuid).",
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "open": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/MentionOpen"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "What clicking the mention opens."
              },
              "summary": {
                "description": "One line for an agent: what the item is and its state. Defaults to the label, hint, description and fields.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "updated_at": {
                "description": "RFC 3339 time of the item's last change.",
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "required": [
              "id",
              "label"
            ],
            "type": "object"
          }
        },
        "title": "Nullable_MentionView"
      }
    },
    {
      "description": "Search live tickets for the chat @kanban mention menu, by key (KAN-12, 12) or title words; best match first.",
      "metadata": {
        "internal": true,
        "trace_hidden": true
      },
      "name": "kanban::mention::search",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "MentionSearchContext": {
            "description": "The chat a search was asked from.",
            "properties": {
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "working_dir": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "type": "object"
          }
        },
        "description": "Input of a provider's search function.",
        "properties": {
          "context": {
            "anyOf": [
              {
                "$ref": "#/definitions/MentionSearchContext"
              },
              {
                "type": "null"
              }
            ],
            "description": "Where the search was asked from, for providers that rank by it."
          },
          "limit": {
            "description": "Maximum rows (default 8, at most 50).",
            "format": "uint",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "query": {
            "default": "",
            "description": "What the user typed after `@<name>:`. Empty asks for the most relevant recent items.",
            "type": "string"
          }
        },
        "title": "MentionSearchRequest",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "MentionItem": {
            "description": "One search row.",
            "properties": {
              "color": {
                "description": "Overrides the provider's color for this row.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "description": "One line after the label (\"in progress · high\").",
                "type": [
                  "string",
                  "null"
                ]
              },
              "hint": {
                "description": "A short human handle shown before the label (\"KAN-12\").",
                "type": [
                  "string",
                  "null"
                ]
              },
              "icon": {
                "description": "Overrides the provider's icon for this row.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "description": "The id written into the token; stable for the item's lifetime.",
                "type": "string"
              },
              "label": {
                "description": "The item's name (\"Fix login redirect\").",
                "type": "string"
              }
            },
            "required": [
              "id",
              "label"
            ],
            "type": "object"
          }
        },
        "description": "Output of a provider's search function, best match first.",
        "properties": {
          "items": {
            "items": {
              "$ref": "#/definitions/MentionItem"
            },
            "type": "array"
          }
        },
        "required": [
          "items"
        ],
        "title": "MentionSearchResponse",
        "type": "object"
      }
    },
    {
      "description": "Internal: re-read the kanban configuration after an operator change.",
      "metadata": {
        "internal": true
      },
      "name": "kanban::on-config-change",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Trigger payload for `<worker>::on-config-change`. Advisory only: handlers re-fetch the authoritative value and ignore it, so a direct call can never inject config.",
        "properties": {
          "id": {
            "default": null,
            "description": "Configuration id that changed.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "OnConfigChangeEvent",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Ack returned by the internal `<worker>::on-config-change` handler.",
        "properties": {
          "ok": {
            "type": "boolean"
          }
        },
        "required": [
          "ok"
        ],
        "title": "OnConfigChangeResponse",
        "type": "object"
      }
    },
    {
      "description": "Archive a project. Its tickets stay; an archived project refuses new tickets. Archiving an archived project changes nothing.",
      "metadata": {},
      "name": "kanban::project::archive",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Project or release id, or its name.",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "EntityRefInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ProjectStatus": {
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          }
        },
        "description": "A named group of tickets (a product area), independent of releases.",
        "properties": {
          "archived_at": {
            "default": null,
            "description": "Set when archived; an archived project refuses new tickets.",
            "type": [
              "string",
              "null"
            ]
          },
          "color": {
            "default": null,
            "description": "blue | purple | teal | green | amber | rose.",
            "type": [
              "string",
              "null"
            ]
          },
          "counts": {
            "additionalProperties": {
              "format": "uint64",
              "minimum": 0,
              "type": "integer"
            },
            "type": "object"
          },
          "created_at": {
            "type": "string"
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "done": {
            "description": "Of those, the ones in the done column.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "lead": {
            "default": null,
            "description": "Agent profile id, or `user`.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "Unique among projects (trimmed, ASCII case ignored).",
            "type": "string"
          },
          "source_label": {
            "default": null,
            "description": "The label this project was converted from, if any.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/definitions/ProjectStatus"
          },
          "total": {
            "description": "Live tickets in the project.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "counts",
          "created_at",
          "done",
          "id",
          "name",
          "status",
          "total",
          "updated_at"
        ],
        "title": "ProjectView",
        "type": "object"
      }
    },
    {
      "description": "Create a project: a named group of tickets with an optional lead and color. Names are unique (ASCII case ignored).",
      "metadata": {},
      "name": "kanban::project::create",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "color": {
            "default": null,
            "description": "blue | purple | teal | green | amber | rose.",
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "lead": {
            "default": null,
            "description": "Agent profile id, or `user`.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "Required; unique among projects (ASCII case ignored).",
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "ProjectCreateInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ProjectStatus": {
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          }
        },
        "description": "A named group of tickets (a product area), independent of releases.",
        "properties": {
          "archived_at": {
            "default": null,
            "description": "Set when archived; an archived project refuses new tickets.",
            "type": [
              "string",
              "null"
            ]
          },
          "color": {
            "default": null,
            "description": "blue | purple | teal | green | amber | rose.",
            "type": [
              "string",
              "null"
            ]
          },
          "counts": {
            "additionalProperties": {
              "format": "uint64",
              "minimum": 0,
              "type": "integer"
            },
            "type": "object"
          },
          "created_at": {
            "type": "string"
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "done": {
            "description": "Of those, the ones in the done column.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "lead": {
            "default": null,
            "description": "Agent profile id, or `user`.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "Unique among projects (trimmed, ASCII case ignored).",
            "type": "string"
          },
          "source_label": {
            "default": null,
            "description": "The label this project was converted from, if any.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/definitions/ProjectStatus"
          },
          "total": {
            "description": "Live tickets in the project.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "counts",
          "created_at",
          "done",
          "id",
          "name",
          "status",
          "total",
          "updated_at"
        ],
        "title": "ProjectView",
        "type": "object"
      }
    },
    {
      "description": "List projects with their live ticket counts (total, done, per column). Archived projects only with include_archived.",
      "metadata": {},
      "name": "kanban::project::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "include_archived": {
            "default": false,
            "description": "Include archived projects. Default false.",
            "type": "boolean"
          }
        },
        "title": "ProjectListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ProjectStatus": {
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          },
          "ProjectView": {
            "description": "A named group of tickets (a product area), independent of releases.",
            "properties": {
              "archived_at": {
                "default": null,
                "description": "Set when archived; an archived project refuses new tickets.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "color": {
                "default": null,
                "description": "blue | purple | teal | green | amber | rose.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "counts": {
                "additionalProperties": {
                  "format": "uint64",
                  "minimum": 0,
                  "type": "integer"
                },
                "type": "object"
              },
              "created_at": {
                "type": "string"
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "done": {
                "description": "Of those, the ones in the done column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "lead": {
                "default": null,
                "description": "Agent profile id, or `user`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "description": "Unique among projects (trimmed, ASCII case ignored).",
                "type": "string"
              },
              "source_label": {
                "default": null,
                "description": "The label this project was converted from, if any.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "$ref": "#/definitions/ProjectStatus"
              },
              "total": {
                "description": "Live tickets in the project.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "counts",
              "created_at",
              "done",
              "id",
              "name",
              "status",
              "total",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "projects": {
            "items": {
              "$ref": "#/definitions/ProjectView"
            },
            "type": "array"
          }
        },
        "required": [
          "projects"
        ],
        "title": "ProjectList",
        "type": "object"
      }
    },
    {
      "description": "Put an archived project back. Restoring a project that is not archived changes nothing.",
      "metadata": {},
      "name": "kanban::project::restore",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Project or release id, or its name.",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "EntityRefInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ProjectStatus": {
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          }
        },
        "description": "A named group of tickets (a product area), independent of releases.",
        "properties": {
          "archived_at": {
            "default": null,
            "description": "Set when archived; an archived project refuses new tickets.",
            "type": [
              "string",
              "null"
            ]
          },
          "color": {
            "default": null,
            "description": "blue | purple | teal | green | amber | rose.",
            "type": [
              "string",
              "null"
            ]
          },
          "counts": {
            "additionalProperties": {
              "format": "uint64",
              "minimum": 0,
              "type": "integer"
            },
            "type": "object"
          },
          "created_at": {
            "type": "string"
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "done": {
            "description": "Of those, the ones in the done column.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "lead": {
            "default": null,
            "description": "Agent profile id, or `user`.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "Unique among projects (trimmed, ASCII case ignored).",
            "type": "string"
          },
          "source_label": {
            "default": null,
            "description": "The label this project was converted from, if any.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/definitions/ProjectStatus"
          },
          "total": {
            "description": "Live tickets in the project.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "counts",
          "created_at",
          "done",
          "id",
          "name",
          "status",
          "total",
          "updated_at"
        ],
        "title": "ProjectView",
        "type": "object"
      }
    },
    {
      "description": "Rename a project or change its description, lead or color (null clears lead and color). The id accepts the project id or its name.",
      "metadata": {},
      "name": "kanban::project::update",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "color": {
            "default": null,
            "description": "blue | purple | teal | green | amber | rose, or null to clear.",
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Project id or name.",
            "type": "string"
          },
          "lead": {
            "default": null,
            "description": "Agent profile id, `user`, or null to clear.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ],
        "title": "ProjectUpdateInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ProjectStatus": {
            "enum": [
              "active",
              "archived"
            ],
            "type": "string"
          }
        },
        "description": "A named group of tickets (a product area), independent of releases.",
        "properties": {
          "archived_at": {
            "default": null,
            "description": "Set when archived; an archived project refuses new tickets.",
            "type": [
              "string",
              "null"
            ]
          },
          "color": {
            "default": null,
            "description": "blue | purple | teal | green | amber | rose.",
            "type": [
              "string",
              "null"
            ]
          },
          "counts": {
            "additionalProperties": {
              "format": "uint64",
              "minimum": 0,
              "type": "integer"
            },
            "type": "object"
          },
          "created_at": {
            "type": "string"
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "done": {
            "description": "Of those, the ones in the done column.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "lead": {
            "default": null,
            "description": "Agent profile id, or `user`.",
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "Unique among projects (trimmed, ASCII case ignored).",
            "type": "string"
          },
          "source_label": {
            "default": null,
            "description": "The label this project was converted from, if any.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/definitions/ProjectStatus"
          },
          "total": {
            "description": "Live tickets in the project.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "counts",
          "created_at",
          "done",
          "id",
          "name",
          "status",
          "total",
          "updated_at"
        ],
        "title": "ProjectView",
        "type": "object"
      }
    },
    {
      "description": "Create a release with an optional target date (YYYY-MM-DD). Names are unique (ASCII case ignored). Its status is derived from its tickets.",
      "metadata": {},
      "name": "kanban::release::create",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "description": "Required; unique among releases (ASCII case ignored).",
            "type": "string"
          },
          "target_at": {
            "default": null,
            "description": "Target date, `YYYY-MM-DD`.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "name"
        ],
        "title": "ReleaseCreateInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ReleaseProjectCount": {
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "project_id": {
                "description": "`null` for tickets in no project.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "ReleaseStatus": {
            "enum": [
              "planned",
              "active",
              "released"
            ],
            "type": "string"
          }
        },
        "description": "A deliverable that tickets are filed under. Its status is derived from the tickets; only `released_at` is stored.",
        "properties": {
          "counts": {
            "additionalProperties": {
              "format": "uint64",
              "minimum": 0,
              "type": "integer"
            },
            "type": "object"
          },
          "created_at": {
            "type": "string"
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "done": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "description": "Unique among releases (trimmed, ASCII case ignored).",
            "type": "string"
          },
          "projects": {
            "description": "Per-project breakdown, biggest first; `project_id: null` is \"no project\".",
            "items": {
              "$ref": "#/definitions/ReleaseProjectCount"
            },
            "type": "array"
          },
          "released_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "reopened": {
            "description": "Not-done live tickets of a released release (0 while it is not released).",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "source_label": {
            "default": null,
            "description": "The label this release was converted from, if any.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/definitions/ReleaseStatus"
              }
            ],
            "description": "released: `released_at` is set. active: some live ticket is outside the first column. planned: otherwise."
          },
          "target_at": {
            "default": null,
            "description": "`YYYY-MM-DD`.",
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "counts",
          "created_at",
          "done",
          "id",
          "name",
          "projects",
          "reopened",
          "status",
          "total",
          "updated_at"
        ],
        "title": "ReleaseView",
        "type": "object"
      }
    },
    {
      "description": "Delete a release no ticket references (soft-deleted tickets included).",
      "metadata": {},
      "name": "kanban::release::delete",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Project or release id, or its name.",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "EntityRefInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "deleted": {
            "type": "boolean"
          }
        },
        "required": [
          "deleted"
        ],
        "title": "Deleted",
        "type": "object"
      }
    },
    {
      "description": "List releases by target date then name, each with its derived status (planned, active, released), live ticket counts, how many tickets were reopened after shipping, and a per-project breakdown.",
      "metadata": {},
      "name": "kanban::release::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "title": "Empty",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ReleaseProjectCount": {
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "project_id": {
                "description": "`null` for tickets in no project.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "ReleaseStatus": {
            "enum": [
              "planned",
              "active",
              "released"
            ],
            "type": "string"
          },
          "ReleaseView": {
            "description": "A deliverable that tickets are filed under. Its status is derived from the tickets; only `released_at` is stored.",
            "properties": {
              "counts": {
                "additionalProperties": {
                  "format": "uint64",
                  "minimum": 0,
                  "type": "integer"
                },
                "type": "object"
              },
              "created_at": {
                "type": "string"
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "name": {
                "description": "Unique among releases (trimmed, ASCII case ignored).",
                "type": "string"
              },
              "projects": {
                "description": "Per-project breakdown, biggest first; `project_id: null` is \"no project\".",
                "items": {
                  "$ref": "#/definitions/ReleaseProjectCount"
                },
                "type": "array"
              },
              "released_at": {
                "default": null,
                "type": [
                  "string",
                  "null"
                ]
              },
              "reopened": {
                "description": "Not-done live tickets of a released release (0 while it is not released).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source_label": {
                "default": null,
                "description": "The label this release was converted from, if any.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "allOf": [
                  {
                    "$ref": "#/definitions/ReleaseStatus"
                  }
                ],
                "description": "released: `released_at` is set. active: some live ticket is outside the first column. planned: otherwise."
              },
              "target_at": {
                "default": null,
                "description": "`YYYY-MM-DD`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "counts",
              "created_at",
              "done",
              "id",
              "name",
              "projects",
              "reopened",
              "status",
              "total",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "releases": {
            "items": {
              "$ref": "#/definitions/ReleaseView"
            },
            "type": "array"
          }
        },
        "required": [
          "releases"
        ],
        "title": "ReleaseList",
        "type": "object"
      }
    },
    {
      "description": "Clear a release's released date. Tickets moved out when it shipped do not come back.",
      "metadata": {},
      "name": "kanban::release::reopen",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Project or release id, or its name.",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "EntityRefInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ReleaseProjectCount": {
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "project_id": {
                "description": "`null` for tickets in no project.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "ReleaseStatus": {
            "enum": [
              "planned",
              "active",
              "released"
            ],
            "type": "string"
          }
        },
        "description": "A deliverable that tickets are filed under. Its status is derived from the tickets; only `released_at` is stored.",
        "properties": {
          "counts": {
            "additionalProperties": {
              "format": "uint64",
              "minimum": 0,
              "type": "integer"
            },
            "type": "object"
          },
          "created_at": {
            "type": "string"
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "done": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "description": "Unique among releases (trimmed, ASCII case ignored).",
            "type": "string"
          },
          "projects": {
            "description": "Per-project breakdown, biggest first; `project_id: null` is \"no project\".",
            "items": {
              "$ref": "#/definitions/ReleaseProjectCount"
            },
            "type": "array"
          },
          "released_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "reopened": {
            "description": "Not-done live tickets of a released release (0 while it is not released).",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "source_label": {
            "default": null,
            "description": "The label this release was converted from, if any.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/definitions/ReleaseStatus"
              }
            ],
            "description": "released: `released_at` is set. active: some live ticket is outside the first column. planned: otherwise."
          },
          "target_at": {
            "default": null,
            "description": "`YYYY-MM-DD`.",
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "counts",
          "created_at",
          "done",
          "id",
          "name",
          "projects",
          "reopened",
          "status",
          "total",
          "updated_at"
        ],
        "title": "ReleaseView",
        "type": "object"
      }
    },
    {
      "description": "Mark a release as released. Live tickets that are not done must go somewhere: move_open_to is another unreleased release (id or name), or null to take them out of any release; it is required when there are such tickets. Returns the release and the tickets moved.",
      "metadata": {},
      "name": "kanban::release::ship",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Release id or name.",
            "type": "string"
          },
          "move_open_to": {
            "default": null,
            "description": "Where the live tickets that are not done go: another release's id or name, or null to take them out of any release. Required when there are such tickets; the target must exist and not be released.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ],
        "title": "ReleaseShipInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ReleaseProjectCount": {
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "project_id": {
                "description": "`null` for tickets in no project.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "ReleaseStatus": {
            "enum": [
              "planned",
              "active",
              "released"
            ],
            "type": "string"
          },
          "ReleaseView": {
            "description": "A deliverable that tickets are filed under. Its status is derived from the tickets; only `released_at` is stored.",
            "properties": {
              "counts": {
                "additionalProperties": {
                  "format": "uint64",
                  "minimum": 0,
                  "type": "integer"
                },
                "type": "object"
              },
              "created_at": {
                "type": "string"
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "id": {
                "type": "string"
              },
              "name": {
                "description": "Unique among releases (trimmed, ASCII case ignored).",
                "type": "string"
              },
              "projects": {
                "description": "Per-project breakdown, biggest first; `project_id: null` is \"no project\".",
                "items": {
                  "$ref": "#/definitions/ReleaseProjectCount"
                },
                "type": "array"
              },
              "released_at": {
                "default": null,
                "type": [
                  "string",
                  "null"
                ]
              },
              "reopened": {
                "description": "Not-done live tickets of a released release (0 while it is not released).",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "source_label": {
                "default": null,
                "description": "The label this release was converted from, if any.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "allOf": [
                  {
                    "$ref": "#/definitions/ReleaseStatus"
                  }
                ],
                "description": "released: `released_at` is set. active: some live ticket is outside the first column. planned: otherwise."
              },
              "target_at": {
                "default": null,
                "description": "`YYYY-MM-DD`.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "counts",
              "created_at",
              "done",
              "id",
              "name",
              "projects",
              "reopened",
              "status",
              "total",
              "updated_at"
            ],
            "type": "object"
          },
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketRow": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketSummary"
              },
              {
                "$ref": "#/definitions/TicketBrief"
              }
            ],
            "description": "One list row: the summary by default, the brief when `compact` was asked."
          },
          "TicketSummary": {
            "description": "The board-card projection of a ticket.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "type": "string"
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "labels": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "created_at",
              "id",
              "key",
              "labels",
              "order",
              "priority",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "moved": {
            "description": "The tickets that were not done and moved out of the release.",
            "items": {
              "$ref": "#/definitions/TicketRow"
            },
            "type": "array"
          },
          "release": {
            "$ref": "#/definitions/ReleaseView"
          }
        },
        "required": [
          "moved",
          "release"
        ],
        "title": "ShipResult",
        "type": "object"
      }
    },
    {
      "description": "Rename a release or change its description or target date (null clears the date). The id accepts the release id or its name.",
      "metadata": {},
      "name": "kanban::release::update",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Release id or name.",
            "type": "string"
          },
          "name": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "target_at": {
            "default": null,
            "description": "Target date `YYYY-MM-DD`, or null to clear.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ],
        "title": "ReleaseUpdateInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "ReleaseProjectCount": {
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "project_id": {
                "description": "`null` for tickets in no project.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "ReleaseStatus": {
            "enum": [
              "planned",
              "active",
              "released"
            ],
            "type": "string"
          }
        },
        "description": "A deliverable that tickets are filed under. Its status is derived from the tickets; only `released_at` is stored.",
        "properties": {
          "counts": {
            "additionalProperties": {
              "format": "uint64",
              "minimum": 0,
              "type": "integer"
            },
            "type": "object"
          },
          "created_at": {
            "type": "string"
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "done": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "id": {
            "type": "string"
          },
          "name": {
            "description": "Unique among releases (trimmed, ASCII case ignored).",
            "type": "string"
          },
          "projects": {
            "description": "Per-project breakdown, biggest first; `project_id: null` is \"no project\".",
            "items": {
              "$ref": "#/definitions/ReleaseProjectCount"
            },
            "type": "array"
          },
          "released_at": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "reopened": {
            "description": "Not-done live tickets of a released release (0 while it is not released).",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "source_label": {
            "default": null,
            "description": "The label this release was converted from, if any.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/definitions/ReleaseStatus"
              }
            ],
            "description": "released: `released_at` is set. active: some live ticket is outside the first column. planned: otherwise."
          },
          "target_at": {
            "default": null,
            "description": "`YYYY-MM-DD`.",
            "type": [
              "string",
              "null"
            ]
          },
          "total": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "counts",
          "created_at",
          "done",
          "id",
          "name",
          "projects",
          "reopened",
          "status",
          "total",
          "updated_at"
        ],
        "title": "ReleaseView",
        "type": "object"
      }
    },
    {
      "description": "Create a ticket on the board and return it; pass compact to get back only its compact row (key, title, status, priority, assignee, comment_count). Optionally make it a subtask (parent), or file it under a project or release (id or name).",
      "metadata": {},
      "name": "kanban::ticket::create",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "actor": {
            "default": null,
            "description": "Who is making the change. Defaults to `user`.",
            "type": [
              "string",
              "null"
            ]
          },
          "assignee": {
            "default": null,
            "description": "Agent profile id.",
            "type": [
              "string",
              "null"
            ]
          },
          "compact": {
            "default": false,
            "description": "Return the compact row (key, title, status, priority, assignee, comment_count) instead of the full ticket.",
            "type": "boolean"
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "labels": {
            "default": null,
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "parent": {
            "default": null,
            "description": "Make the new ticket a subtask of this ticket (uuid or key). The parent must be live and not a subtask itself.",
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "project": {
            "default": null,
            "description": "Project id or name. An archived project refuses new tickets.",
            "type": [
              "string",
              "null"
            ]
          },
          "release": {
            "default": null,
            "description": "Release id or name.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "default": null,
            "description": "Column id; defaults to the configured default_status.",
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "description": "Required.",
            "type": "string"
          }
        },
        "required": [
          "title"
        ],
        "title": "TicketCreateInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "anyOf": [
          {
            "$ref": "#/definitions/TicketView"
          },
          {
            "$ref": "#/definitions/TicketBrief"
          }
        ],
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketView": {
            "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
            "properties": {
              "activity": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Activity"
                },
                "type": "array"
              },
              "assignee": {
                "default": null,
                "description": "Agent profile id.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "children": {
                "description": "Live subtasks by ticket number.",
                "items": {
                  "$ref": "#/definitions/Brief"
                },
                "type": "array"
              },
              "comments": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Comment"
                },
                "type": "array"
              },
              "created_at": {
                "type": "string"
              },
              "created_by": {
                "default": "user",
                "type": "string"
              },
              "deleted_at": {
                "default": null,
                "description": "Set when soft-deleted; the row stays on disk.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "id": {
                "description": "Internal uuid.",
                "type": "string"
              },
              "key": {
                "description": "Human-readable id, e.g. KAN-1.",
                "type": "string"
              },
              "labels": {
                "default": [],
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "default": 0,
                "description": "Position inside its column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Brief"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
              },
              "parent_id": {
                "default": null,
                "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "progress": {
                "$ref": "#/definitions/Progress"
              },
              "project_id": {
                "default": null,
                "description": "Uuid of the project (several tickets, independent of the release).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "default": null,
                "description": "Uuid of the release.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Column id.",
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "children",
              "created_at",
              "id",
              "key",
              "priority",
              "progress",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "description": "What a ticket write returns: the full ticket, or its compact row when the request asked for `compact`.",
        "title": "TicketWritten"
      }
    },
    {
      "description": "Soft-delete a ticket: it leaves the board but stays in the board file. Deleting a deleted ticket changes nothing.",
      "metadata": {},
      "name": "kanban::ticket::delete",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "actor": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "TicketRefInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          }
        },
        "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
        "properties": {
          "activity": {
            "default": [],
            "items": {
              "$ref": "#/definitions/Activity"
            },
            "type": "array"
          },
          "assignee": {
            "default": null,
            "description": "Agent profile id.",
            "type": [
              "string",
              "null"
            ]
          },
          "children": {
            "description": "Live subtasks by ticket number.",
            "items": {
              "$ref": "#/definitions/Brief"
            },
            "type": "array"
          },
          "comments": {
            "default": [],
            "items": {
              "$ref": "#/definitions/Comment"
            },
            "type": "array"
          },
          "created_at": {
            "type": "string"
          },
          "created_by": {
            "default": "user",
            "type": "string"
          },
          "deleted_at": {
            "default": null,
            "description": "Set when soft-deleted; the row stays on disk.",
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "id": {
            "description": "Internal uuid.",
            "type": "string"
          },
          "key": {
            "description": "Human-readable id, e.g. KAN-1.",
            "type": "string"
          },
          "labels": {
            "default": [],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "order": {
            "default": 0,
            "description": "Position inside its column.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "parent": {
            "anyOf": [
              {
                "$ref": "#/definitions/Brief"
              },
              {
                "type": "null"
              }
            ],
            "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
          },
          "parent_id": {
            "default": null,
            "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "type": "string"
          },
          "progress": {
            "$ref": "#/definitions/Progress"
          },
          "project_id": {
            "default": null,
            "description": "Uuid of the project (several tickets, independent of the release).",
            "type": [
              "string",
              "null"
            ]
          },
          "release_id": {
            "default": null,
            "description": "Uuid of the release.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "Column id.",
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "children",
          "created_at",
          "id",
          "key",
          "priority",
          "progress",
          "status",
          "title",
          "updated_at"
        ],
        "title": "TicketView",
        "type": "object"
      }
    },
    {
      "description": "Read one ticket with its comments and activity, its parent, its live subtasks and their progress. The id accepts the internal uuid or the human-readable key.",
      "metadata": {},
      "name": "kanban::ticket::get",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "TicketGetInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          }
        },
        "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
        "properties": {
          "activity": {
            "default": [],
            "items": {
              "$ref": "#/definitions/Activity"
            },
            "type": "array"
          },
          "assignee": {
            "default": null,
            "description": "Agent profile id.",
            "type": [
              "string",
              "null"
            ]
          },
          "children": {
            "description": "Live subtasks by ticket number.",
            "items": {
              "$ref": "#/definitions/Brief"
            },
            "type": "array"
          },
          "comments": {
            "default": [],
            "items": {
              "$ref": "#/definitions/Comment"
            },
            "type": "array"
          },
          "created_at": {
            "type": "string"
          },
          "created_by": {
            "default": "user",
            "type": "string"
          },
          "deleted_at": {
            "default": null,
            "description": "Set when soft-deleted; the row stays on disk.",
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "id": {
            "description": "Internal uuid.",
            "type": "string"
          },
          "key": {
            "description": "Human-readable id, e.g. KAN-1.",
            "type": "string"
          },
          "labels": {
            "default": [],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "order": {
            "default": 0,
            "description": "Position inside its column.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "parent": {
            "anyOf": [
              {
                "$ref": "#/definitions/Brief"
              },
              {
                "type": "null"
              }
            ],
            "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
          },
          "parent_id": {
            "default": null,
            "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "type": "string"
          },
          "progress": {
            "$ref": "#/definitions/Progress"
          },
          "project_id": {
            "default": null,
            "description": "Uuid of the project (several tickets, independent of the release).",
            "type": [
              "string",
              "null"
            ]
          },
          "release_id": {
            "default": null,
            "description": "Uuid of the release.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "Column id.",
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "children",
          "created_at",
          "id",
          "key",
          "priority",
          "progress",
          "status",
          "title",
          "updated_at"
        ],
        "title": "TicketView",
        "type": "object"
      }
    },
    {
      "description": "Cross-references of one ticket: the tickets it mentions and the tickets that mention it, found by key (KAN-12) in titles, descriptions and comments. Rows are the same summaries kanban::ticket::list returns. The id accepts the internal uuid or the human-readable key.",
      "metadata": {},
      "name": "kanban::ticket::links",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "TicketLinksInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketRow": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketSummary"
              },
              {
                "$ref": "#/definitions/TicketBrief"
              }
            ],
            "description": "One list row: the summary by default, the brief when `compact` was asked."
          },
          "TicketSummary": {
            "description": "The board-card projection of a ticket.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "type": "string"
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "labels": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "created_at",
              "id",
              "key",
              "labels",
              "order",
              "priority",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "mentioned_by": {
            "description": "Live tickets whose title, description or comments mention this ticket's key, by ticket number.",
            "items": {
              "$ref": "#/definitions/TicketRow"
            },
            "type": "array"
          },
          "mentions": {
            "description": "Live tickets whose key appears in this ticket's title, description or comments, by ticket number.",
            "items": {
              "$ref": "#/definitions/TicketRow"
            },
            "type": "array"
          }
        },
        "required": [
          "mentioned_by",
          "mentions"
        ],
        "title": "TicketLinks",
        "type": "object"
      }
    },
    {
      "description": "List tickets, optionally narrowed to a column, an assignee, an updated_since timestamp, a parent (its subtasks), a project or a release (empty string = none); compact rows carry only key, title, status, priority, assignee and comment_count.",
      "metadata": {},
      "name": "kanban::ticket::list",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "assignee": {
            "default": null,
            "description": "Agent profile id; empty string matches unassigned.",
            "type": [
              "string",
              "null"
            ]
          },
          "compact": {
            "default": false,
            "description": "Return the compact row (key, title, status, priority, assignee, comment_count) instead of the full summary.",
            "type": "boolean"
          },
          "include_deleted": {
            "default": false,
            "type": "boolean"
          },
          "limit": {
            "default": null,
            "format": "uint64",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "parent_id": {
            "default": null,
            "description": "Only the subtasks of this ticket (uuid or key); empty string = tickets that are not subtasks.",
            "type": [
              "string",
              "null"
            ]
          },
          "project_id": {
            "default": null,
            "description": "Only tickets in this project (id or name); empty string = tickets in no project.",
            "type": [
              "string",
              "null"
            ]
          },
          "release_id": {
            "default": null,
            "description": "Only tickets in this release (id or name); empty string = tickets in no release.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "default": null,
            "description": "Column id.",
            "type": [
              "string",
              "null"
            ]
          },
          "updated_since": {
            "default": null,
            "description": "Only tickets updated after this ISO timestamp.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "TicketListInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketRow": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketSummary"
              },
              {
                "$ref": "#/definitions/TicketBrief"
              }
            ],
            "description": "One list row: the summary by default, the brief when `compact` was asked."
          },
          "TicketSummary": {
            "description": "The board-card projection of a ticket.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "type": "string"
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "labels": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "created_at",
              "id",
              "key",
              "labels",
              "order",
              "priority",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "count": {
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "tickets": {
            "items": {
              "$ref": "#/definitions/TicketRow"
            },
            "type": "array"
          }
        },
        "required": [
          "count",
          "tickets"
        ],
        "title": "TicketList",
        "type": "object"
      }
    },
    {
      "description": "Move a ticket to a column (and position inside it); used by the board drag and drop. Moving a ticket to where it already is changes nothing; a deleted ticket cannot be moved. Returns the ticket, or only its compact row with compact.",
      "metadata": {},
      "name": "kanban::ticket::move",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "actor": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "compact": {
            "default": false,
            "description": "Return the compact row (key, title, status, priority, assignee, comment_count) instead of the full ticket.",
            "type": "boolean"
          },
          "id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          },
          "index": {
            "default": null,
            "description": "Zero-based position in the target column.",
            "format": "uint64",
            "minimum": 0,
            "type": [
              "integer",
              "null"
            ]
          },
          "status": {
            "description": "Target column id.",
            "type": "string"
          }
        },
        "required": [
          "id",
          "status"
        ],
        "title": "TicketMoveInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "anyOf": [
          {
            "$ref": "#/definitions/TicketView"
          },
          {
            "$ref": "#/definitions/TicketBrief"
          }
        ],
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketView": {
            "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
            "properties": {
              "activity": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Activity"
                },
                "type": "array"
              },
              "assignee": {
                "default": null,
                "description": "Agent profile id.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "children": {
                "description": "Live subtasks by ticket number.",
                "items": {
                  "$ref": "#/definitions/Brief"
                },
                "type": "array"
              },
              "comments": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Comment"
                },
                "type": "array"
              },
              "created_at": {
                "type": "string"
              },
              "created_by": {
                "default": "user",
                "type": "string"
              },
              "deleted_at": {
                "default": null,
                "description": "Set when soft-deleted; the row stays on disk.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "id": {
                "description": "Internal uuid.",
                "type": "string"
              },
              "key": {
                "description": "Human-readable id, e.g. KAN-1.",
                "type": "string"
              },
              "labels": {
                "default": [],
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "default": 0,
                "description": "Position inside its column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Brief"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
              },
              "parent_id": {
                "default": null,
                "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "progress": {
                "$ref": "#/definitions/Progress"
              },
              "project_id": {
                "default": null,
                "description": "Uuid of the project (several tickets, independent of the release).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "default": null,
                "description": "Uuid of the release.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Column id.",
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "children",
              "created_at",
              "id",
              "key",
              "priority",
              "progress",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "description": "What a ticket write returns: the full ticket, or its compact row when the request asked for `compact`.",
        "title": "TicketWritten"
      }
    },
    {
      "description": "Put a soft-deleted ticket back on the board. A subtask whose parent is deleted, gone or a subtask itself comes back detached (activity on both tickets, an event for each). Restoring a ticket that is not deleted changes nothing.",
      "metadata": {},
      "name": "kanban::ticket::restore",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "actor": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          }
        },
        "required": [
          "id"
        ],
        "title": "TicketRefInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          }
        },
        "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
        "properties": {
          "activity": {
            "default": [],
            "items": {
              "$ref": "#/definitions/Activity"
            },
            "type": "array"
          },
          "assignee": {
            "default": null,
            "description": "Agent profile id.",
            "type": [
              "string",
              "null"
            ]
          },
          "children": {
            "description": "Live subtasks by ticket number.",
            "items": {
              "$ref": "#/definitions/Brief"
            },
            "type": "array"
          },
          "comments": {
            "default": [],
            "items": {
              "$ref": "#/definitions/Comment"
            },
            "type": "array"
          },
          "created_at": {
            "type": "string"
          },
          "created_by": {
            "default": "user",
            "type": "string"
          },
          "deleted_at": {
            "default": null,
            "description": "Set when soft-deleted; the row stays on disk.",
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "default": "",
            "type": "string"
          },
          "id": {
            "description": "Internal uuid.",
            "type": "string"
          },
          "key": {
            "description": "Human-readable id, e.g. KAN-1.",
            "type": "string"
          },
          "labels": {
            "default": [],
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "order": {
            "default": 0,
            "description": "Position inside its column.",
            "format": "uint64",
            "minimum": 0,
            "type": "integer"
          },
          "parent": {
            "anyOf": [
              {
                "$ref": "#/definitions/Brief"
              },
              {
                "type": "null"
              }
            ],
            "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
          },
          "parent_id": {
            "default": null,
            "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "type": "string"
          },
          "progress": {
            "$ref": "#/definitions/Progress"
          },
          "project_id": {
            "default": null,
            "description": "Uuid of the project (several tickets, independent of the release).",
            "type": [
              "string",
              "null"
            ]
          },
          "release_id": {
            "default": null,
            "description": "Uuid of the release.",
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "description": "Column id.",
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "updated_at": {
            "type": "string"
          }
        },
        "required": [
          "children",
          "created_at",
          "id",
          "key",
          "priority",
          "progress",
          "status",
          "title",
          "updated_at"
        ],
        "title": "TicketView",
        "type": "object"
      }
    },
    {
      "description": "Update a ticket field (title, description, status, priority, assignee, labels, parent, project, release) and return the ticket (compact returns only its compact row). To change labels pass add_labels / remove_labels (they edit the current labels, so concurrent edits do not overwrite each other); labels replaces the whole set. parent makes the ticket a subtask (one level: the parent must be live and not a subtask, a ticket with subtasks cannot become one) and null detaches it; the activity is recorded on both tickets. project and release take an id or a name; null clears. A patch that changes nothing records and fires nothing.",
      "metadata": {},
      "name": "kanban::ticket::update",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "properties": {
          "actor": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "add_labels": {
            "default": null,
            "description": "Labels to add to the current ones; one already present (ASCII case ignored) is skipped. Applied after `labels` and `remove_labels`.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "assignee": {
            "default": null,
            "description": "Agent profile id, or null to unassign.",
            "type": [
              "string",
              "null"
            ]
          },
          "compact": {
            "default": false,
            "description": "Return the compact row (key, title, status, priority, assignee, comment_count) instead of the full ticket.",
            "type": "boolean"
          },
          "description": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "id": {
            "description": "Ticket uuid or human-readable key (KAN-1, case-insensitive).",
            "type": "string"
          },
          "labels": {
            "default": null,
            "description": "Replaces the whole label set. Prefer add_labels / remove_labels: they edit the labels the ticket has when the write runs, so concurrent label edits do not overwrite each other.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "parent": {
            "default": null,
            "description": "Ticket uuid or key to make this ticket a subtask of, or null to detach it. One level only: the parent must be live and not a subtask, and a ticket with subtasks cannot become one.",
            "type": [
              "string",
              "null"
            ]
          },
          "priority": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "project": {
            "default": null,
            "description": "Project id or name, or null to clear. An archived project refuses new tickets.",
            "type": [
              "string",
              "null"
            ]
          },
          "release": {
            "default": null,
            "description": "Release id or name, or null to clear.",
            "type": [
              "string",
              "null"
            ]
          },
          "remove_labels": {
            "default": null,
            "description": "Labels to drop from the current ones (ASCII case ignored). Applied after `labels`, before `add_labels`.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "status": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "default": null,
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id"
        ],
        "title": "TicketUpdateInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "anyOf": [
          {
            "$ref": "#/definitions/TicketView"
          },
          {
            "$ref": "#/definitions/TicketBrief"
          }
        ],
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "TicketBrief": {
            "description": "The compact projection: what an agent needs to recognise a ticket in a list (`compact: true` on `kanban::board::get` / `kanban::ticket::list`). About a third of a summary's size — no uuid, timestamps, labels or order.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "TicketView": {
            "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
            "properties": {
              "activity": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Activity"
                },
                "type": "array"
              },
              "assignee": {
                "default": null,
                "description": "Agent profile id.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "children": {
                "description": "Live subtasks by ticket number.",
                "items": {
                  "$ref": "#/definitions/Brief"
                },
                "type": "array"
              },
              "comments": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Comment"
                },
                "type": "array"
              },
              "created_at": {
                "type": "string"
              },
              "created_by": {
                "default": "user",
                "type": "string"
              },
              "deleted_at": {
                "default": null,
                "description": "Set when soft-deleted; the row stays on disk.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "id": {
                "description": "Internal uuid.",
                "type": "string"
              },
              "key": {
                "description": "Human-readable id, e.g. KAN-1.",
                "type": "string"
              },
              "labels": {
                "default": [],
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "default": 0,
                "description": "Position inside its column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Brief"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
              },
              "parent_id": {
                "default": null,
                "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "progress": {
                "$ref": "#/definitions/Progress"
              },
              "project_id": {
                "default": null,
                "description": "Uuid of the project (several tickets, independent of the release).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "default": null,
                "description": "Uuid of the release.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Column id.",
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "children",
              "created_at",
              "id",
              "key",
              "priority",
              "progress",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "description": "What a ticket write returns: the full ticket, or its compact row when the request asked for `compact`.",
        "title": "TicketWritten"
      }
    },
    {
      "description": "Serve the kanban worker's injected console UI assets (content function for its console:script / console:style / console:module triggers).",
      "metadata": {
        "internal": true
      },
      "name": "kanban::ui-content",
      "request_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Input of the content function: the console asks for one asset by path.",
        "properties": {
          "path": {
            "description": "The asset path from the trigger config (e.g. `state/page.js`).",
            "type": "string"
          }
        },
        "required": [
          "path"
        ],
        "title": "UiContentInput",
        "type": "object"
      },
      "response_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "description": "Output of the content function.",
        "properties": {
          "content": {
            "description": "The asset source, verbatim.",
            "type": "string"
          },
          "content_type": {
            "description": "MIME type the console should serve the asset with.",
            "type": "string"
          }
        },
        "required": [
          "content",
          "content_type"
        ],
        "title": "UiContentResult",
        "type": "object"
      }
    }
  ],
  "triggers": [
    {
      "description": "Fires after every kanban mutation with the whole ticket (parent, subtasks and progress included). Config: { ticket_id?, parent_id?, release_id?, events?, metadata? } — ticket_id and parent_id match the uuid or the human key (parent_id also fires for each subtask); release_id is a release id or name (fires for tickets in it and for tickets moved out of it).",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "TicketDetail": {
            "description": "How much of the ticket a delivery carries. `full` is the ticket with its comments and activity inline; `summary` is the board-card projection (key, title, status, priority, assignee, labels, order, timestamps, comment_count) — the right choice for an agent that re-reads the ticket with `kanban::ticket::get` anyway.",
            "enum": [
              "full",
              "summary"
            ],
            "type": "string"
          }
        },
        "properties": {
          "events": {
            "description": "Event names to fire for (ticket.created, ticket.updated, ticket.moved, ticket.deleted, ticket.restored, comment.created). Omit for all.",
            "items": {
              "type": "string"
            },
            "type": [
              "array",
              "null"
            ]
          },
          "metadata": {
            "description": "Metadata attached to every invocation this binding receives."
          },
          "parent_id": {
            "description": "Ticket uuid or human key of a parent: fires for the parent itself and for each of its subtasks.",
            "type": [
              "string",
              "null"
            ]
          },
          "release_id": {
            "description": "Release id or name: fires for a ticket in the release and for a ticket a write just moved out of it.",
            "type": [
              "string",
              "null"
            ]
          },
          "ticket": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketDetail"
              },
              {
                "type": "null"
              }
            ],
            "description": "`full` (default) delivers the whole ticket; `summary` only its board-card fields."
          },
          "ticket_id": {
            "description": "Ticket uuid or human key. Omit to fire for every ticket.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "ChangeConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "kanban:change",
      "return_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "TicketPayload": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketView"
              },
              {
                "$ref": "#/definitions/TicketSummary"
              }
            ],
            "description": "The `ticket` field of a delivery: the whole ticket, or its summary when the binding asked for `ticket: summary`."
          },
          "TicketSummary": {
            "description": "The board-card projection of a ticket.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "type": "string"
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "labels": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "created_at",
              "id",
              "key",
              "labels",
              "order",
              "priority",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          },
          "TicketView": {
            "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
            "properties": {
              "activity": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Activity"
                },
                "type": "array"
              },
              "assignee": {
                "default": null,
                "description": "Agent profile id.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "children": {
                "description": "Live subtasks by ticket number.",
                "items": {
                  "$ref": "#/definitions/Brief"
                },
                "type": "array"
              },
              "comments": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Comment"
                },
                "type": "array"
              },
              "created_at": {
                "type": "string"
              },
              "created_by": {
                "default": "user",
                "type": "string"
              },
              "deleted_at": {
                "default": null,
                "description": "Set when soft-deleted; the row stays on disk.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "id": {
                "description": "Internal uuid.",
                "type": "string"
              },
              "key": {
                "description": "Human-readable id, e.g. KAN-1.",
                "type": "string"
              },
              "labels": {
                "default": [],
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "default": 0,
                "description": "Position inside its column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Brief"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
              },
              "parent_id": {
                "default": null,
                "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "progress": {
                "$ref": "#/definitions/Progress"
              },
              "project_id": {
                "default": null,
                "description": "Uuid of the project (several tickets, independent of the release).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "default": null,
                "description": "Uuid of the release.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Column id.",
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "children",
              "created_at",
              "id",
              "key",
              "priority",
              "progress",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "activity": {
            "anyOf": [
              {
                "$ref": "#/definitions/Activity"
              },
              {
                "type": "null"
              }
            ]
          },
          "at": {
            "type": "string"
          },
          "comment": {
            "anyOf": [
              {
                "$ref": "#/definitions/Comment"
              },
              {
                "type": "null"
              }
            ]
          },
          "event": {
            "type": "string"
          },
          "ticket": {
            "$ref": "#/definitions/TicketPayload"
          },
          "ticket_id": {
            "type": "string"
          },
          "ticket_key": {
            "type": "string"
          }
        },
        "required": [
          "at",
          "event",
          "ticket",
          "ticket_id",
          "ticket_key"
        ],
        "title": "ChangeEvent",
        "type": "object"
      }
    },
    {
      "description": "Fires when a comment is added, optionally filtered by author. Config: { ticket_id? | parent_id?, author?, exclude_author?, root_only? } — ticket_id is one ticket; parent_id is a ticket and all its subtasks (uuid or human key).",
      "invocation_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "TicketDetail": {
            "description": "How much of the ticket a delivery carries. `full` is the ticket with its comments and activity inline; `summary` is the board-card projection (key, title, status, priority, assignee, labels, order, timestamps, comment_count) — the right choice for an agent that re-reads the ticket with `kanban::ticket::get` anyway.",
            "enum": [
              "full",
              "summary"
            ],
            "type": "string"
          }
        },
        "properties": {
          "author": {
            "description": "Fire only for comments by exactly this author.",
            "type": [
              "string",
              "null"
            ]
          },
          "exclude_author": {
            "description": "Fire for every author except this one — pass your own profile id.",
            "type": [
              "string",
              "null"
            ]
          },
          "parent_id": {
            "description": "Ticket uuid or human key of a parent: comments on the parent or on any of its subtasks.",
            "type": [
              "string",
              "null"
            ]
          },
          "root_only": {
            "description": "Ignore replies; fire only for top-level comments.",
            "type": [
              "boolean",
              "null"
            ]
          },
          "ticket": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketDetail"
              },
              {
                "type": "null"
              }
            ],
            "description": "`full` (default) delivers the whole ticket; `summary` only its board-card fields."
          },
          "ticket_id": {
            "description": "Ticket uuid or human key. Give this or `parent_id`; a binding with neither never fires.",
            "type": [
              "string",
              "null"
            ]
          }
        },
        "title": "CommentConfig",
        "type": "object"
      },
      "metadata": {},
      "name": "kanban:comment",
      "return_schema": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "definitions": {
          "Activity": {
            "properties": {
              "actor": {
                "type": "string"
              },
              "at": {
                "type": "string"
              },
              "changes": {
                "items": {
                  "$ref": "#/definitions/Change"
                },
                "type": [
                  "array",
                  "null"
                ]
              },
              "comment_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "seq": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "ticket_id": {
                "type": "string"
              },
              "type": {
                "description": "ticket.created | ticket.updated | ticket.moved | ticket.deleted | ticket.restored | comment.created",
                "type": "string"
              }
            },
            "required": [
              "actor",
              "at",
              "id",
              "seq",
              "ticket_id",
              "type"
            ],
            "type": "object"
          },
          "Brief": {
            "description": "A ticket in short: what a parent shows for each child and a child for its parent, and what a project or release page lists.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "key",
              "priority",
              "status",
              "title"
            ],
            "type": "object"
          },
          "Change": {
            "properties": {
              "field": {
                "type": "string"
              },
              "from": true,
              "to": true
            },
            "required": [
              "field",
              "from",
              "to"
            ],
            "type": "object"
          },
          "Comment": {
            "properties": {
              "author": {
                "description": "Agent profile id, or `user`.",
                "type": "string"
              },
              "body": {
                "type": "string"
              },
              "created_at": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "parent_id": {
                "default": null,
                "description": "Set for a reply; replies stay one level deep.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "ticket_id": {
                "type": "string"
              }
            },
            "required": [
              "author",
              "body",
              "created_at",
              "id",
              "ticket_id"
            ],
            "type": "object"
          },
          "Progress": {
            "description": "Live children in the done column over live children.",
            "properties": {
              "done": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "total": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              }
            },
            "required": [
              "done",
              "total"
            ],
            "type": "object"
          },
          "TicketPayload": {
            "anyOf": [
              {
                "$ref": "#/definitions/TicketView"
              },
              {
                "$ref": "#/definitions/TicketSummary"
              }
            ],
            "description": "The `ticket` field of a delivery: the whole ticket, or its summary when the binding asked for `ticket: summary`."
          },
          "TicketSummary": {
            "description": "The board-card projection of a ticket.",
            "properties": {
              "assignee": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "comment_count": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "created_at": {
                "type": "string"
              },
              "deleted_at": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "id": {
                "type": "string"
              },
              "key": {
                "type": "string"
              },
              "labels": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "project_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "comment_count",
              "created_at",
              "id",
              "key",
              "labels",
              "order",
              "priority",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          },
          "TicketView": {
            "description": "Every ticket a function returns or an event carries: the stored ticket plus its relations, derived from the board at the moment of the read or write.",
            "properties": {
              "activity": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Activity"
                },
                "type": "array"
              },
              "assignee": {
                "default": null,
                "description": "Agent profile id.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "children": {
                "description": "Live subtasks by ticket number.",
                "items": {
                  "$ref": "#/definitions/Brief"
                },
                "type": "array"
              },
              "comments": {
                "default": [],
                "items": {
                  "$ref": "#/definitions/Comment"
                },
                "type": "array"
              },
              "created_at": {
                "type": "string"
              },
              "created_by": {
                "default": "user",
                "type": "string"
              },
              "deleted_at": {
                "default": null,
                "description": "Set when soft-deleted; the row stays on disk.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "default": "",
                "type": "string"
              },
              "id": {
                "description": "Internal uuid.",
                "type": "string"
              },
              "key": {
                "description": "Human-readable id, e.g. KAN-1.",
                "type": "string"
              },
              "labels": {
                "default": [],
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "order": {
                "default": 0,
                "description": "Position inside its column.",
                "format": "uint64",
                "minimum": 0,
                "type": "integer"
              },
              "parent": {
                "anyOf": [
                  {
                    "$ref": "#/definitions/Brief"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The parent, deleted or not; `null` for a ticket that is not a subtask."
              },
              "parent_id": {
                "default": null,
                "description": "Uuid of the parent ticket. One level deep: a parent has no parent.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "priority": {
                "type": "string"
              },
              "progress": {
                "$ref": "#/definitions/Progress"
              },
              "project_id": {
                "default": null,
                "description": "Uuid of the project (several tickets, independent of the release).",
                "type": [
                  "string",
                  "null"
                ]
              },
              "release_id": {
                "default": null,
                "description": "Uuid of the release.",
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "description": "Column id.",
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "updated_at": {
                "type": "string"
              }
            },
            "required": [
              "children",
              "created_at",
              "id",
              "key",
              "priority",
              "progress",
              "status",
              "title",
              "updated_at"
            ],
            "type": "object"
          }
        },
        "properties": {
          "at": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "comment": {
            "$ref": "#/definitions/Comment"
          },
          "event": {
            "description": "Always `comment.created`.",
            "type": "string"
          },
          "ticket": {
            "$ref": "#/definitions/TicketPayload"
          },
          "ticket_id": {
            "type": "string"
          },
          "ticket_key": {
            "type": "string"
          }
        },
        "required": [
          "at",
          "author",
          "comment",
          "event",
          "ticket",
          "ticket_id",
          "ticket_key"
        ],
        "title": "CommentEvent",
        "type": "object"
      }
    }
  ]
}
```
