tix — a terminal issue tracker
tix is a kanban-style issue tracker that lives entirely in your repository: issues are plain Markdown + YAML frontmatter files. There is no database, no server, and no proprietary format — you get a terminal UI (TUI), a full CLI, and optional GitLab/Linear sync in one Rust binary.
Heavily AI assisted. This project was designed, specified, and built end-to-end with AI agents under a spec-kitty-SPEC style governed workflow (spec → plan → tasks → implement → review → merge). All 6 work packages, 27 functional requirements, and 33 subtasks were implemented and reviewed by AI agents (
implementer-ivan,reviewer-renata) steered by a human operator.
- The human owns the repo and the decisions; the agents wrote the code.
- Every feature went through a review cycle with real gate checks (
cargo test,clippy -D warnings,cargo fmt) before merge.- The full mission trail lives in
kitty-specs/issue-tracker-tui-01M27PTN/(spec, plan, tasks, acceptance matrix, review report, retrospective) if you want an audit-grade record of how the software was produced.- How Spec Kitty and tix interoperate (
.kittify/, missions, merge drivers, task mirroring) is documented indocs/spec-kitty-integration.md.- OpenSpec task mirroring and
tix init-agentsdetection output are documented indocs/openspec-integration.md.- The MCP surface, including
link_issue, lives indocs/mcp.md.- Task-oriented user guides for cycles and triage live under
docs/for-users/.tix init-agentsitself writes an AI-agent usage section intoAGENTS.md, so other agents can operate this tracker autonomously.
Install
The crate is published to crates.io as tix-tracker (the binary is still
called tix). See docs/packaging.md for the full
maintainer workflow (crates.io account, publishing, binary releases).
# or, with prebuilt binaries (fast, no Rust toolchain needed):
Local development installs (same binary) work too:
Requires a Rust toolchain (edition 2024, MSRV 1.85). The only runtime
dependency is your favorite editor for tix edit.
Quickstart
CLI
| Command | What it does |
|---|---|
tix init |
Create a default tix.toml |
tix create |
New issue (title/body/status/priority/assignee/labels/estimate/files) |
tix new |
The same, in the TUI: opens the board with the create-issue form already open |
tix list |
Table of issues, optional --status filter, --columns selection and --with-archived scope |
tix view <issue> |
Full issue (frontmatter + body) |
tix status |
Board summary: count per column + total |
tix search <query> |
Fuzzy-search issue titles and bodies |
tix lint |
Validate issue frontmatter and report diagnostics |
tix render |
Render the current board once without entering the TUI |
tix edit <issue> |
Edit fields via flags, or full file via $EDITOR (--files) |
tix move <issue> <column> |
Move between columns; done records a commit hash (auto-captured from HEAD, or --commit auto/<hash>) |
tix link <a> <b> --type <kind> |
Add a typed relationship — blocks, blocked_by, duplicates, or related (required) |
tix link list / tix link edit <id> / tix link remove <id> |
Inspect, retarget/re-kind, and remove relationships by their stable id |
tix unlink <a> <b> --type <kind> |
Remove a relationship of that kind |
tix chain <issue> |
Print the transitive blocks chain an issue holds up |
tix archive <issue> / tix restore <issue> |
Hide an issue from the default views, or bring it back — by dot-prefixing its filename (details) |
tix bulk edit|link|unlink|archive |
Batch over a selection; preview is the default, --apply writes (details) |
tix cycle ... / tix milestone ... |
Create, list, edit, rename, and delete planning items |
tix workflow categories | show | transitions |
Print the status-category vocabulary, how this board resolves each column, and which transitions are permitted |
tix delete <issue> --yes |
Remove an issue file |
tix export --target gitlab|linear |
Push local issues to a remote tracker |
tix import --target gitlab|linear |
Pull open issues from a remote tracker |
tix init-agents |
Write/refresh the AI usage section in AGENTS.md |
An issue can be referenced by its number, a unique filename fragment, or a unique title fragment.
tix init-agents detects .kittify/ and openspec/ independently and writes
the corresponding tracking blocks into AGENTS.md. When it sees them, it also
prints the matching detection lines to stdout so scripts can tell which blocks
were emitted.
TUI
Launch with tix (no arguments) from the repository root. The complete, configurable key reference is docs/key-bindings.md; the list below is only a default overview.
tix new starts the same session with the create-issue form already open — one
command to file an issue instead of "start the board, then press n".
- Arrows / h/l — move between columns, j/k — select issues
- PgUp/PgDn — move a screen of cards at a time in the current column
- Enter — view the full issue, n — new issue form
- Shift+C — clone the selected issue into that form, prefilled
- ? — the keybindings overlay: / searches it (substring first, fuzzy when there is no substring match), j/k or ↓/↑ a row, PgUp/PgDn a page, the wheel; the first page in a longer list says how many rows it is showing
- e — edit the issue in your
$EDITOR - d — delete (with confirmation)
- m — manage cycles and milestones (browse with ↓/↑ or j/k, switch segments with Tab/Shift+Tab, n create, e edit, x delete; q/Esc returns to the board)
- / — filter the live board; a bare number such as
/55opens issue 55, and the active-filter row shows the current query - q / Esc — quit
In the issue detail view:
- n — link this issue to another, then name the kind. The target is a selector
(
3,#3, or a title fragment) and the kind is required — there is no default, so a relationship can never be created untypeable - j / k (or ↓ / ↑) — move the relationship cursor; the selected edge is
marked with
>in thelinks:row - E — re-kind or retarget the selected relationship
- d — remove the selected relationship, after a confirmation that names the edge
- c — comment, a — attach an asset, ? — help
Every one of these goes through the same service the CLI and MCP use, and reports
the same wording, so a relationship created in the TUI is byte-identical to one
created with tix link.
The TUI also supports mouse interaction: click to select cards and controls, double-click to open details, drag cards between columns, right-click for issue actions, scroll long views, and hover for visual feedback. Keyboard operation remains available.
In dialogs (f filter criteria, n new-issue form, s settings)
the Tab / Shift+Tab or ↑ / ↓ keys move between fields/rows.
Date fields are masked and picker-assisted: type digits (separators are added
automatically) to enter a YYYY-MM-DD date, or press Enter (when empty) or
F2 to open a calendar (arrows/PgUp/PgDn navigate, Enter selects, c
clears, Esc cancels).
External sync
tix export/import talks to GitLab through the glab CLI and to Linear
through linear-cli. If the tool is missing, tix fails with a clear,
actionable error. Repeated exports are idempotent: issues are matched by their
gitlab_id / linear_id, and conflicts resolve in favor of the newer
updated timestamp.
Storage format
Every issue is a file like docs/issues/alice/2026-09-11-my-title.md:
number: 1
title: My issue
status: todo
priority: high
labels: bug, docs
assignee: @alice
links: [12]
files: [openspec/specs/tui-keybindings/spec.md]
created: 2026-09-11
updated: 2026-09-11
The body is plain markdown — screenshots and diagrams work as usual.
Human-readable, human-edit-able, git-friendly. No data is locked away.
tix re-renders the frontmatter in its own style on write. A hand-edited file
is not preserved byte for byte: list items are re-indented flush left, and
strings may come back with different quoting.
$ cat issue.md | grep -A 3 cycles:
cycles:
- Alpha # you wrote it indented
$ tix view 1 > copy.md && diff issue.md copy.md
< - Alpha
< - Beta
> - Alpha
> - Beta
Nothing is lost — every field survives and re-parses to the same issue — but
tix view <n> > copy.md will show a diff on a hand-edited file. This is
pre-existing and general (cycles, milestones, and relationships all
normalise); it is noted here because "human-editable" implies your formatting
survives, and for the frontmatter it does not. The ## History section is
preserved verbatim instead — see kitty-specs/…/data-model.md §8.
Custom frontmatter keys
A top-level key tix does not recognise is preserved, byte for byte, and
serde_yaml's re-quoting never touches it. It is spliced back into the
frontmatter on every write, after the known keys, in sorted order.
$ cat issue.md
---
number: 1
title: My issue
build_id: ci-4471 # not a tix field
---
$ tix edit 1 --priority high && grep build_id issue.md
build_id: ci-4471 # still there
tix lint reports an unrecognised key as a warning — printed, counted, and
exit 0. A file carrying a custom key is a valid file, so failing a build over
it would report a non-problem as a broken one:
$ tix lint; echo "exit: $?"
warning[W-tix-lint]: unknown top-level frontmatter key `build_id`: it is preserved
on write but nothing reads it. If that is deliberate, declare it in
`[custom_attributes]` in tix.toml; if not, it is probably a typo.
Lint OK with 1 warning: 1 issues checked.
exit: 0
The warning exists because an unrecognised key is usually a typo. titel:
round-trips silently and is read by nothing, which is a far worse failure than a
key that was dropped. Declare the ones you mean:
[]
= ["build_id", "reviewed_by"]
Names only, top-level only — there is no dotted-path syntax, because the thing being declared is a top-level key. Declared keys do not warn.
The retired links: key is the exception that proves the rule: it is
recognised but never preserved, reported by its own retired_links_key
finding and then dropped, so the typed-relationship migration is not silently
undone. See the breaking-change note below.
files records repository-relative governing specifications or supporting
documents; relationships records the typed edges below. tix create,
tix edit, tix link, and tix unlink maintain these fields, and the TUI detail
view displays them.
Relationships
Four kinds, and the difference is not cosmetic — only two of them affect the board:
--type |
Meaning | Blocks the target? |
|---|---|---|
blocks |
this issue blocks the target | yes |
blocked_by |
the inverse view of the same edge | yes (stored canonically as blocks) |
duplicates |
the target restates this issue | no |
related |
a non-directional association | no |
blocks is directional and reads left to right: tix link 12 34 --type blocks
means "12 blocks 34", so 34 is blocked. A related edge is order-normalised and
stored once, in the canonical source's file — so an issue's own relationships
field is deliberately partial. tix link list and the derived views read the whole
graph, so both endpoints see it.
$ tix link 12 34 --type blocks
Linked #12 blocks #34 (rel_01JQ8Z4W2M6N7P9R3S5T7V9X1)
$ tix view 34 --show relationships
...
## Relationships
- #12 --blocks--> #34 Refactor store [rel_01JQ8Z4W2M6N7P9R3S5T7V9X1]
## Dependency chain
No dependency chain for #34
## Blocked
- blocked: Blocked by #12 Fix the parser
Blocked state. An issue with an open inbound blocks edge is blocked. That
is visible in tix list --columns blocked --show relationships, in
tix view --show relationships, and as a marker on the TUI card.
The blocked-move guard. Moving a blocked issue into the board's completion column is refused unless you confirm it:
$ tix move 34 done
warning: blocked: Blocked by #12 Fix the parser
tix: invalid input: blocked: Blocked by #12 Fix the parser — moving to 'done' with
open blockers requires an explicit confirmation; re-run with --yes to record the
decision
$ tix move 34 done --yes --commit abc1234
Acknowledged blocked move to done
Note that tix move <n> done also needs a commit hash. In a git repository it
auto-captures HEAD; outside one you must pass --commit or --commit auto, so
the refusal above is about the blockers and the commit is a separate requirement.
tix edit --status is a move too and answers the same way. Confirming records a
blocked_move_acknowledged entry in the issue's ## History, so the decision is
auditable rather than implicit. The TUI asks before it writes, and the MCP
move_issue tool takes yes.
⚠️ Breaking change: the links: frontmatter key is gone
The generic links: [12] field has been removed and replaced by the typed
relationships field above.
- There is no migration, and no compatibility layer. A file that still carries
links:parses, but the value is never read as relationship data — it is not converted torelatedor to anything else. tix lintreports such a file asretired_links_keyand tells you what to do.- The key disappears on the next write of that issue.
tix create --links,tix edit --links, and the MCPlinksargument are rejected, not ignored. An integration that used to pass one now gets an explicit error namingtix link/ thelink_issuetool, rather than a success response and silently lost relationships.
To convert, read the old list and re-create each edge:
$ tix link 12 34 --type related # was: links: [34]
$ tix link 34 35 --type blocks # was: links: [35] on 34
An empty links: [] is not reported — it has nothing to lose, and flagging it
would make tix lint reject every file written before this change.
Migrating a whole repository at once:
# report which files still carry the retired key
|
Parent issues
An issue can have one parent. That makes a decomposition tree — this is part
of that — and it is deliberately not a dependency edge. "Must happen before"
is a --type blocks relationship; "part of" is a parent. Using a
parent to mean ordering is the mistake this distinction exists to prevent.
$ tix parent show 3
Parent of #3
parent: none
children: 2
completed: 1/2 (direct children only)
Children
#7 Refactor store
#9 Add a parser
One parent, never a list. parent: holds a single number. Two parents are not
expressible, not merely rejected.
What is refused. An issue cannot be its own parent, and a parent change that would close a cycle is refused. A refused change writes nothing — no file, no number, no history entry — and the reason is the same text whichever surface you asked on (CLI, TUI or MCP).
Rollups count direct children only. 3/2 over a subtree and 3/2 over direct
children are different numbers wearing the same clothes, so the completion line
always names the population: (direct children only).
Deleting a parent keeps its children. The reference survives, and tix lint
reports it as unresolved_parent naming the child and the missing target, so it can
be repaired:
$ tix delete 3 --yes
#7 Refactor store - keeps `parent: 3`
$ tix lint
error[E-tix-lint]: invalid unresolved_parent value '3'
--> herbert-maschke/2026-09-29-refactor-store.md:8:9
|
8 | parent: 3
| ^ 2's parent #3 no longer exists (the reference is kept so it can be repaired)
Cascading a delete is never what happens: silently destroying a subtree because one node went away is not a thing this tool does.
Batch editing
tix bulk does the same work over many issues with one preview and one
confirmation. It is the same plan, the same typed patch and the same apply the
single-issue path uses — there is no second implementation, on any surface.
# What would change? Nothing is written.
# Do it.
# Link every selected issue to one target.
Selecting
Name exactly one of three grammars. They are different languages, so a bulk operation will not guess which issues you meant:
| Flag | Grammar |
|---|---|
--select 1,2,3 |
explicit: numbers, filename fragments or title fragments, comma-separated |
--filter 'status:todo label:bug' |
the same structured filter the filter modal builds |
--search 'fix crash' |
the same search the search box runs |
--scope chooses active (default), all (active and archived) or archived.
What can change
Nine fields, and the list is closed: status, priority, assignee, labels,
start-date, due-date, estimate, cycles, milestones.
--clear <field> empties one of them — anything except status, because a batch
that empties a column has no target column. One per invocation; a second is
refused rather than quietly dropped.
A blank flag value means unchanged, not empty. --estimate "" leaves the
estimate alone. To empty a field, name it in --clear. This is the single most
common surprise, and it is deliberate: the alternative makes it impossible to
clear a field with a flag and impossible to tell "I left it blank on purpose" from
"I did not mention it".
title and body are deliberately not in the list. A batch title rewrite is
the one bulk operation nobody has asked for and everybody would regret, and a
closed list is what makes "what can a batch do" a single answer.
Preview, apply, and what a partial batch means
Preview is the default; --apply writes. A preview touches no file — not the
issue, not its History, not a relationship — and cancelling one changes nothing,
because the only write in the whole path is the apply.
Each selected issue is processed independently. A refused or failed item is reported and does not roll back the successful ones; there is no all-or-nothing mode. Every selected issue gets exactly one outcome line:
| Status | Meaning |
|---|---|
applied |
the requested state was written |
no-op |
it was already in that state — nothing written, nothing appended |
rejected |
refused at plan time; nothing was attempted |
failed |
the write failed; whatever succeeded before it stands |
Re-running is idempotent, and says so: a second run reports no-op rather than
repeating the work or quietly succeeding.
A partial batch exits non-zero. Zero means everything requested happened, and the output does not claim otherwise.
Pairs and archive
tix bulk link / unlink need --target and --type. The type is the
relationship service's own vocabulary — there is no default kind, so an untyped
pair is never created by accident — and each selected issue becomes its own pair
with the target.
tix bulk archive is not available. Single-issue archive and restore exist
(details); doing it to a selection, with a preview and per-item
outcomes, is a separate mission's scope, and a second archive implementation is
exactly what this tool does not want. The request is refused by name, per selected
issue, so you can see what it would have archived.
The TUI and MCP
B opens the batch surface on the board; space marks a card, x unmarks it,
X clears the selection. p previews, y arms, y again writes, esc
cancels. A batch is bounded at 200 selected issues, and the bound is in the header
so you can see where you are.
Over MCP the same work is three tools — bulk_preview (read-only),
bulk_apply (requires confirm: true) and bulk_capabilities (what this build
supports). The schema is generated from the same whitelist the CLI and the docs
above are generated from, so a field cannot be advertised on one surface and
ignored on another.
Archiving
Archiving takes an issue out of the default views without deleting it. The marker is the filename: the file is renamed in place to a dot-prefixed name.
$ tix archive 1
Archived herbert-maschke/.2026-10-01-old-thing.md
$ tix list
NUM TITLE STATUS PRIORITY
2 Live thing todo medium
$ tix show 1 # still resolves, by number, title or slug
number: 1
title: Old thing
$ tix restore 1
Restored herbert-maschke/2026-10-01-old-thing.md
Nothing else moves. No file is deleted or moved between directories, and
number, status, created and every frontmatter field stay byte-identical —
archiving is not a softer tix delete, it is a different thing, and the file is
still there. Each operation appends one ## History event (archived /
restored), and a redundant call is refused by name rather than recorded twice:
$ tix restore 1
tix: invalid input: not_archived: issue #1 (herbert-maschke/2026-10-01-old-thing.md) is already active
An archived issue keeps its number: tix create allocates above every issue,
archived ones included, so a number is never recycled onto a different issue.
Seeing them again. Default views do not read archived files at all — that is
the point, and it is what keeps a repository with thousands of archived issues as
fast to open as an empty one. --with-archived is the scope flag, and
archived:true|false is the criterion:
$ tix list --with-archived
NUM TITLE STATUS PRIORITY ARCHIVED
1 Old thing todo medium ARCHIVED
2 Live thing todo medium -
$ tix list --with-archived --filter 'archived:true'
NUM TITLE STATUS PRIORITY ARCHIVED
1 Old thing todo medium ARCHIVED
$ tix search --with-archived "Old thing"
#1 Old thing — todo (herbert-maschke/.2026-10-01-old-thing.md)
The scope flag is what loads the archived set; the criterion only narrows what is
loaded, so --filter 'archived:true' on its own has nothing to match. In the TUI,
a toggles them and archived cards are marked [archived] (keys).
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:
[]
= ["triage", "wip", "review", "shipped", "abandoned"]
= "shipped"
[]
= "backlog"
= "active"
= "active"
= "completed"
= "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:
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.
Triage inbox
Unreviewed work can enter a triage inbox before it joins the normal workflow.
The inbox is a view over your own board columns — not a second tracker, a second
workflow, or a queue database — and every decision is one History event in the
issue file. User guide: docs/for-users/triage.md;
design reference: docs/triage.md.
It is opt-in: the inbox exists because a [triage] section does — and tix init
writes one, so a repository you have just created has an inbox already. Removing
the section is what turns it off, and every tix triage command then refuses by
naming it.
[]
= "triage" # required — the inbox column
= "wontfix" # required — must map to the `canceled` category
# accepted_status = "todo" # optional — the default accept target
# deferred_status = "triage" # optional — defaults to entry_status
$ tix triage list
inbox: 4 entries (4 awaiting, 4 deferred)
NUM AGE TITLE
1 2465d Alpha waits
2 unknown Bravo legacy
3 1d Charlie recent
4 2d Delta
awaiting counts the entry column and deferred the deferred one. The fixture
above configures no deferred_status, so the two are the same column and both
figures are 4 — they answer different questions ("is it in the inbox" and "is it
in the deferred column"), and a project that separates them sees them diverge. The
ages depend on the day you run it; the counts and the unknown cell do not.
The TUI opens the same queue with t and decides with a/d/u/f (and p
for everything marked); MCP exposes it as triage_list, triage_status,
triage_preview and triage_apply (the write requires confirm: true). All three
read one snapshot through the same service, so the counts, the outcomes and the
error kinds are the same document.
Imported issues enter through an explicit, reported policy rather than bypassing
review: tix import --target gitlab --triage-policy entry. Absent the flag, an
import routes into the inbox only when the project has a [triage] section, and a
project without one imports exactly as it did before the inbox existed.
Configuration
tix.toml (at the repository root) defines the board columns, the issues
directory, per-board card wrapping and ordering, optional [export] targets,
and optional TUI [keybindings]. Card display defaults to wrapped three-line
titles sorted newest-first; these settings can be changed in the TUI settings
modal. See docs/key-bindings.md for the complete
keyboard syntax and mode/action reference.
= ["todo", "in progress", "done", "mismatch"]
= "docs/issues"
[]
= { = "/path/to/token" }
= { = "", = "" }
Tokens are never serialized back out after configuration.
tix.toml reference
Boards live under [kanban.<name>]; the board named default is the one used by
every command that takes no --board.
| Key | Type | Default | Notes |
|---|---|---|---|
columns |
string[] |
triage, todo, in progress, in review, done, wontfix (what tix init writes); todo, in progress, in review, done as the bare fallback |
The column names. status: holds one of these. |
done_column |
string |
done if present, else the last column |
Which column means finished, for commit clearing. A board whose category_map places completed elsewhere gets its completion answer from the category, not from this key. |
category_map |
table |
absent | <column> = "<category>". Every value must be one of the six vocabulary keys. A key naming a column the board does not configure is a load error. |
show_mismatch |
bool |
false |
Render a Mismatched column for issues in unconfigured columns. |
order_by |
string |
created |
Card ordering within a column. |
title_lines |
integer |
3 |
Card title height. |
wrap_titles |
bool |
true |
Wrap card titles. |
hidden_columns |
string[] |
[] |
Columns collapsed in the TUI; still reachable by filter and search. |
[categories] (top level, optional)
| Key | Type | Default | Notes |
|---|---|---|---|
order |
string[] |
all six keys | The vocabulary, in the project's preferred presentation order. It may be a subset, and it may not contain a word outside the global six — adding a key here is an unknown_category error, not a new category. |
default |
string |
unstarted |
The category an unmapped column resolves to. Must be in order, and must not be completed — otherwise every unmapped column would silently count as finished. |
[triage] (top level, optional)
tix init writes one, so a new repository has an inbox from the start; absent — a
config written before the feature, or one that removed the section — there is no
inbox and every tix triage command refuses, naming the section. Every named column
is validated against the active board through the category service, so a typo is a
load error naming the column rather than an empty queue. See
docs/triage.md.
| Key | Type | Default | Notes |
|---|---|---|---|
entry_status |
string |
required when the section exists | The column that places an issue in the inbox. |
declined_status |
string |
required when the section exists | Where a decline lands. Must resolve to the canceled category. |
accepted_status |
string |
absent | The default target for an accept; --to overrides it per decision. With neither, an accept is refused missing_target rather than guessed. |
deferred_status |
string |
entry_status |
The column an issue sits in while deferred. |
[transitions] (top level, optional)
| Key | Type | Default | Notes |
|---|---|---|---|
terminal_locked |
bool |
true |
Whether leaving a terminal category needs a reason. Only consulted on a board that declares a category_map. |
All three sections are omitted from disk when untouched, so an existing
tix.toml round-trips without acquiring the keys it never had.
Version
Current version: 0.4.0. See CHANGELOG.md.
License
MIT OR Apache-2.0.