tix-tracker 0.12.0

A terminal issue tracker: markdown + YAML frontmatter issues with a kanban TUI and CLI
Documentation
# Workflow status categories

A board's columns are yours. tix does not have a second notion of "state" — a
column name is still what `status:` holds, and there is no database, no hosted
workflow engine and no new file to keep in sync. What this adds is a way to say
what a column **means**, once, and have every surface use that answer.

Map your columns into a **global, closed** vocabulary:

```toml
[kanban.default]
columns = ["triage", "wip", "review", "shipped", "abandoned"]
done_column = "shipped"

[kanban.default.category_map]
triage    = "backlog"
wip       = "active"
review    = "active"
shipped   = "completed"
abandoned = "canceled"
```

| Category | Meaning |
| --- | --- |
| `backlog` | Captured, not started |
| `unstarted` | Ready, not started |
| `active` | In progress |
| `blocked` | Stopped on something |
| `completed` | The work is **done** |
| `canceled` | Terminal, and **not** done |

`completed` and `canceled` are both terminal. Only `completed` is ever counted as
finished — a `canceled` issue reads as "we stopped" in every report, and
`tix show` says so explicitly (`workflow: terminal, not complete`) so nobody has
to infer it from the word.

`tix workflow categories` prints the vocabulary in the order adjacency is
evaluated against. `tix workflow show` prints what each of your columns resolved
to **and where that answer came from** — `category_map` or the safe default.
`tix workflow transitions` prints the permitted moves.

## Columns with no mapping

A column you configure but do not map resolves to the **safe default**,
`unstarted`. That choice is deliberate: it is neither terminal nor complete, so an
unmapped column is never silently counted as finished. `tix lint` reports it as a
single `unmapped_column_default` warning per board, naming every affected column
— one warning rather than one per column, because a board that declares *some*
mappings and leaves the rest is a normal configuration, not a fault.

A column the board does not configure at all is different, and every surface says
so the same way: `(unmapped)` in `tix list --show category`, `(unmapped)` in
`tix show`, `null` over MCP. It is a configuration error, and it is visible
rather than defaulted.

## Transition policy, and how to leave a terminal state

**A board that declares a `category_map` gets the transition policy. A board that
does not, does not.** That split is the whole compatibility story: every existing
repository keeps its completion detection and its commit-clearing rule, and gains
no new refusals. A `category_map` is the signal that you have described your
categories, and describing them is opting in.

The policy itself:

- an issue may not move **backwards** in the vocabulary order;
- an issue in a **terminal** category may not leave it without a reason.

The reason is recorded in the issue's own History, next to the accepted decision:

```sh
tix move 12 review --reason "wrong epic"
tix edit 12 --status review --reason "wrong epic"
```

The TUI has no flags, so it prompts for the same reason and shows the identical
message; MCP takes a `reason` argument with the same meaning. One escape, three
spellings, one recorded decision.

A board whose `category_map` exists but omits `completed` is **refused when the
config loads** — you have said what your categories are and left out the one every
board needs. A board with no `category_map` at all is not refused, because there
is no declared policy to violate.

## Hand-edited files

`tix lint` compares each file's `status:` against the last transition recorded in
its own History and reports a disagreement as `direct_edit_policy_violation`. It
**writes nothing** — no repair, no appended entry, no rewrite. The file stays
exactly as you left it and stays readable, and every other command keeps working
on it. The finding is a report, not a load failure.