# 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, each passing real gate checks
> (`cargo test`, `clippy -D warnings`, `cargo fmt`) before merge. The mission
> trail lives in `kitty-specs/issue-tracker-tui-01M27PTN/`; the integration
> surfaces are documented in
> [`docs/spec-kitty-integration.md`](docs/spec-kitty-integration.md),
> [`docs/openspec-integration.md`](docs/openspec-integration.md) and
> [`docs/mcp.md`](docs/mcp.md). `tix init-agents` writes an AI-agent usage
> section into `AGENTS.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](docs/packaging.md) for the full
maintainer workflow (crates.io account, publishing, binary releases).
```bash
cargo install tix-tracker # build from source via crates.io
# or, with prebuilt binaries (fast, no Rust toolchain needed):
cargo install cargo-binstall --locked # one-time, if you don't have it
cargo binstall tix-tracker
```
Local development installs (same binary) work too:
```bash
cargo install --path .
```
Requires a Rust toolchain (edition 2024, MSRV 1.85). The only runtime
dependency is your favorite editor for `tix edit`.
## Quickstart
```bash
tix init # create the default tix.toml (columns, issues_dir)
tix create --title "First issue" --priority high --labels docs
tix status # board summary: column -> count
tix list # your issues in todo: number, id, title, one line each
tix list --all # every active issue
tix list --columns title,status # choose and order displayed columns
tix ready # unblocked work, highest priority first
tix # open the kanban TUI
```
## CLI
| `tix init` | Create a default `tix.toml` (and add `*.tixbak` / `*.md.bak` to `.gitignore`) |
| `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` | One line per issue — number, id, title — scoped to **your** issues (the git author identity) in the board's entry column; `--all` for every active issue, plus `--status`/`--filter`, `--columns`, `--sort`, `--group`, `--show`, `--format xml\|yml\|toml` and `--with-archived` ([details](docs/for-users/listing.md)) |
| `tix ready` | The next issue to start: unblocked, in a `backlog`/`unstarted` column, highest priority first (then oldest) — so the first row is what to pick up next ([details](docs/for-users/listing.md#tix-ready)) |
| `tix view <issue>` | Full issue (frontmatter + body) |
| `tix status` | Board summary: count per column + total |
| `tix search <query>` | Substring-search issue titles and bodies (case-insensitive; `--with-archived` includes archived issues) |
| `tix lint` | Validate issue frontmatter and report diagnostics |
| `tix render` | Render the current board once without entering the TUI |
| `tix clean` | Remove the backup files `tix` writes beside an issue before it rewrites one — `*.tixbak` and `*.md.bak`; confirms each file unless `-f`/`--force` |
| `tix edit <issue>` | Edit fields via flags, or full file via `$EDITOR` (`--files`) |
| `tix move <issue…> <column>` | Move one or more issues; a selection larger than one previews unless `--yes`; `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](docs/for-users/archiving.md)) |
| `tix bulk edit\|link\|unlink` | Batch over a selection; **preview is the default**, `--apply` writes ([details](docs/for-users/batch-editing.md)) |
| `tix cycle ...` / `tix milestone ...` | Create, list, edit, rename, and delete planning items; creating a cycle warns when its dates overlap another |
| `tix bulk edit\|link\|unlink\|archive` | Batch over a selection; **preview is the default**, `--apply` writes ([details](#batch-editing)) |
| `tix cycle ...` / `tix milestone ...` | Create, list, edit, rename, and delete planning items |
| `tix view list\|show\|create\|update\|duplicate\|delete\|apply\|status\|share` | Save, reuse, and share named filter/sort/group/visibility views ([details](#saved-issue-views)) |
| `tix template ...` | Manage stable-ID issue/form templates: `list`, `show`, `create`, `update`, `duplicate`, `delete` ([details](docs/templates.md)) |
| `tix recurring ...` | Recurring series: `create`, `edit`, `list`, `show`, `detect`, `generate`, `pause`/`resume`/`end`, `skip`/`defer`/`ignore` — generation and every lifecycle change need `--yes` ([details](docs/recurring.md)) |
| `tix cycle schedule ...` | Repeating cycle schedules: `list`, `show`, `create`, `edit`, `delete`, `detect`, `generate`, and the lifecycle verbs `close`/`extend`/`skip`/`pause`/`resume`/`adjust-future`/`ignore` — every mutation needs `--confirm` ([details](docs/cycle-schedules.md)) |
| `tix wip show\|set\|unset\|unit` | Per-column work-in-progress limits: item or estimate ceilings and a `warn`/`block` policy, enforced by every move surface ([details](docs/wip.md)) |
| `tix time add\|edit\|delete\|show\|history\|total` | Per-issue time entries in **whole minutes**, with an optional `YYYY-MM-DD` date, an author and an optional note; every correction and deletion appends one attributable History event ([details](docs/time-tracking.md)) |
| `tix workflow categories \| show \| transitions` | Print the status-category vocabulary, how this board resolves each column, and which transitions are permitted |
| `tix health set\|show\|rollup\|override\|history\|board` | Project and issue health: an audited state per issue, derived rollups over parents, cycles and milestones under a versioned policy, and a portable export ([details](docs/health.md)) |
| `tix initiative create\|edit\|list\|show\|delete\|associate\|dereference` / `tix roadmap list\|show` | Strategic initiatives over the existing registries and a source-attributed roadmap projection ([details](docs/roadmap.md)) |
| `tix delete <issue> --yes` | Remove an issue file |
| `tix export --target gitlab\|linear` | Push local issues to a remote tracker ([details](docs/for-users/external-sync.md)) |
| `tix export --target beads` | Write local issues into a [beads](https://github.com/gastownhall/beads) workspace through `bd` ([details](docs/for-users/beads-integration.md)) |
| `tix import --target gitlab\|linear` | Pull open issues from a remote tracker |
| `tix import --target speckit` | Mirror a local GitHub Spec Kit project as labeled issues and subissues; idempotent |
| `tix import --target beads` | Pull a [beads](https://github.com/gastownhall/beads) workspace into local files through `bd` |
| `tix beads detect\|status\|list\|show\|watch` | Read a beads workspace through `bd`, read-only ([details](docs/for-users/beads-integration.md)) |
| `tix beads push\|pull\|reconcile` | Keep tix and beads in step through `bd` (refused while the mode is `mirror`) |
| `tix init --beads off\|mirror\|sync` | Record the beads mode; `sync` installs the `.beads/hooks` pipe |
| `tix init-agents` | Write/refresh the AI usage section in `AGENTS.md` |
| `tix version` | Print the crate version plus the commit and time this binary was built — how to tell whether `cargo run` actually rebuilt |
An issue can be referenced by number, id, filename fragment, or title fragment.
`tix init-agents` detects `.kittify/`, `openspec/` and `.specify/` 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; `tix new` starts the
same session with the create-issue form already open. A missing `tix.toml` is
offered a one-key escape — create the defaults and continue — instead of being
refused. The complete, configurable
key reference is [`docs/key-bindings.md`](docs/key-bindings.md); the list here is
only a default overview.
A session that reaches the TUI opens a **"did you know?" tips modal** on a random
tip from the list compiled into the binary (`src/templates/tips/`); `←`/`→` walk it,
`r` shows another random one, `Enter`/`Esc` closes it,
and `d` turns the startup tips off by writing `tips_on_startup = false` to
`tix.toml`.
Nine top-level modes share one shell with a vertical navigation tree on the left
(clickable limbs per board, cycle, milestone and template). A mode's function
key switches to it only when the active mode does not bind that key itself, so
`F2` inside a form is still the picker.
- **Arrows / h/l** — move between columns, **j/k** — select issues
- **Enter** — view the full issue, **n** — new issue form
- **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 `/55` opens issue 55, and the active-filter row shows the current query
- **v** — saved views: `Enter` applies, `n` saves the live board, `e` renames, `u` updates, `d` duplicates, `x` deletes ([details](#saved-issue-views))
- **q / Esc** — quit
| New Issue | `F1` | the create-issue form, with an explicit Triage/Todo target | [new issue](docs/for-users/new-issue.md) |
| Triage | `F2` | the triage inbox | [triage](docs/for-users/triage.md) |
| Board | `F3` | the kanban board | [board keys](docs/key-bindings.md#board) |
| Cycles | `F4` | cycles grouped current / future / archived, with statistics | [cycles](docs/for-users/cycles.md) |
| Milestones | `F5` | the milestone registry with per-milestone progress | [config](docs/for-users/config.md) |
| Repos | `F6` | the child repositories this project coordinates | [navigation](docs/for-users/navigation.md) |
| Stats | `F7` | every scope's health, with its contributors and diagnostics; `Tab` switches between the **Health**, **Time** and **Burndown** tabs — the Time tab's totals, entry rows and audit, and the Burndown tab's daily remaining/completed series, coverage markers and end-date forecast | [health](docs/health.md), [time tracking](docs/time-tracking.md), [burndown](docs/burndown-analytics.md) |
| Schedule | `F8` | recurring series and repeating cycle schedules — `Tab` switches the **Series** and **Cycle schedules** tabs | [recurring](docs/recurring.md), [cycle schedules](docs/cycle-schedules.md) |
| Portfolio | `F10` | the project registry, with its Projects, Epics and Roadmap tabs | [projects](docs/for-users/configuration.md#projectsproject-id-top-level-optional) |
| Library | `F11` | saved views and issue/form templates, listed and inspected | [key bindings](docs/key-bindings.md#library) |
| Config | `F12` | dashboards, cycles, milestones, types, and attributes in one surface | [config](docs/for-users/config.md) |
Board highlights: arrows and `h`/`j`/`k` navigate, `PgUp`/`PgDn` page a column,
`Enter` opens an issue, `n`/`F1` creates, `Shift+C` clones, `u` toggles the
sub-issue tree, `e` edits in `$EDITOR`, `V` views read-only, `d` deletes, `m`
manages cycles and milestones, `/` searches the live board, `?` opens searchable
help, `q`/`Esc` quits. In the issue detail view, `n`/`E`/`d` add, re-kind and
remove typed relationships, and `Shift+P` sets, changes or clears the parent —
all through the same service the CLI and MCP use, so a relationship created in
the TUI is byte-identical to one created with `tix link`.
The TUI is mouse-aware (click, double-click, drag, right-click, scroll, hover)
and never sacrifices keyboard operation. Editor round trips are conflict-safe: a
file changed by another writer is never clobbered by a stray keypress — see the
[edit-conflict and edit-repair modals](docs/key-bindings.md#modal-management-modes).
## Storage format
Issues are files under `docs/issues/…` with YAML frontmatter and a Markdown
body. `tix` re-renders the frontmatter in its own style on write (custom keys are
preserved, `## History` verbatim), and the layout, `related_issues`, and custom
frontmatter keys are all configurable. See
[storage format](docs/for-users/storage.md).
## Relationships and parents
Four typed relationship kinds (`blocks`, `blocked_by`, `duplicates`, `related`)
replace the retired `links:` key; only `blocks` drives the blocked-move guard. An
issue has at most one parent — a decomposition tree, not an ordering edge. See
[relationships](docs/for-users/relationships.md) and
[parent issues](docs/for-users/parent-issues.md).
## Projects
A **project** is a first-class delivery container: stable identity, name,
description, status, an optional lead and dates, and a set of member issues with
progress and health. This is the **documented anchor** for mission
`first-class-projects-01M3BYBW`:
- **The anchor is a `[projects.<project-id>]` registry in `tix.toml` plus a
`projects:` list of ids in issue frontmatter.** There is **no** new record
format, **no** database, **no** hosted project service, **no** user-identity
concept, and **no** implicit project inheritance (a child does not join its
parent's projects). Markdown and configuration remain canonical.
- **The id is the identity; the name is a display label.** `tix project create`
allocates a stable generated id (the registry key), and every association
stores that id — so renaming a project changes the label only and never
re-keys anything. A deleted id is never reused.
- **An issue may belong to several projects.** Membership is derived on read,
never stored, so repeated reads of the same Markdown are identical.
```bash
tix project create "Platform hardening" --status active --lead "Alice <alice@example.com>" \
--start-date 2026-09-01 --due-date 2026-12-31
tix project list # one row per project: id, name, member count
tix project show p-3f9a2c # record, progress, health counts, members
tix project edit p-3f9a2c --name "Platform" # rename the label only
tix project delete p-3f9a2c # registry entry only — issues are kept
```
Associate and dissociate:
```bash
tix create "Add retry backoff" --projects p-3f9a2c
tix edit 3 --add-project p-7b1e04 # an issue may carry several
tix edit 3 --remove-project p-7b1e04 # drops only the association
tix edit 3 --projects "" # clears every association
tix project add p-3f9a2c 3 4 5 # batch; each issue independent
tix project remove p-3f9a2c 3
```
Filtering, grouping and views:
```bash
tix search "project:p-3f9a2c" # the same membership predicate everywhere
tix list --project p-3f9a2c
tix list --group project # buckets: the project id, (multiple projects), (no project)
tix list --sort project
tix status --group project # the same buckets in the board summary
```
`tix project show` prints the project's progress and the completed/overdue/
blocked/unassigned health counts, computed once by the shared service and read by
every surface (CLI, the `F10` **Portfolio** page, and MCP) so they cannot
disagree.
**Missing references are never dropped.** Deleting a project removes only the
registry entry; the issues that referenced it keep their ids, `tix lint` reports
each as a `missing_project_reference` naming the issue and the id, and the
`F10` page and MCP surface them too. Repair by associating the issue with a live
project or removing the dangling id (`tix project remove <id> <issue>`). Deleting
a project never deletes an issue.
The registry reference — field names, optionality, id-never-reused and the
empty-section omission — is in the
[`tix.toml` reference](docs/for-users/configuration.md#projectsproject-id-top-level-optional).
## Epics
An **epic** is a cross-cutting delivery container: stable identity, a display
name, optional description, status, owner, dates and a weight, with a set of
member issues, child epics, progress and blocked work. This is the **documented
anchor** for mission `epics-work-items-01M3BYW4`:
- **The anchor is a `[epics.<epic-id>]` registry in `tix.toml` plus an `epics:`
list of ids in issue frontmatter.** There is **no** new record format, **no**
database, **no** hosted planning service, **no** task entity, and **no** second
relationship mechanism. Markdown and configuration remain canonical.
- **The id is the identity; the name is a display label.** `tix epic create`
allocates a stable generated id (the registry key, never a name), and every
reference stores that id — so renaming an epic changes the label only and never
breaks a reference. A deleted id is never reused.
- **An issue may reference several epics** and an epic may have **one** parent
epic. Membership and hierarchy are derived on read, never stored, so repeated
reads of the same Markdown are identical.
- **Tasks are ordinary issues.** A `task`-labelled issue that references an epic
is a member like any other: there is no task file, entity or frontmatter key
(C-001).
- **Milestone and cycle references are name-keyed**, matching how the existing
`[milestones]`/`[cycles]` registries already work; epic `parent` and `projects`
are id-keyed. That asymmetry is deliberate.
```bash
tix epic create "Harden the sync path" --weight 5 --due-date 2026-12-31
tix epic list # one row per epic: id, name, child + member counts
tix epic show e-7c4a91 # hierarchy, progress, weight, blockers, members
tix epic navigate e-7c4a91 # parent, children, ancestors, depth
tix epic edit e-7c4a91 --name "Sync hardening" # rename the label only
tix epic delete e-7c4a91 # registry entry only — members and children are kept
```
Associate and dissociate:
```bash
tix create "Add retry backoff" --epics e-7c4a91
tix edit 3 --add-epic e-7c4a91 # an issue may reference several
tix edit 3 --remove-epic e-7c4a91 # drops only the reference
tix edit 3 --epics "" # clears every reference
tix epic add e-7c4a91 3 4 5 # batch; each issue independent
tix epic remove e-7c4a91 3
```
Filtering, grouping and views:
```bash
tix search "epic:e-7c4a91" # the same membership predicate everywhere
tix list --epic e-7c4a91
tix list --group epic # buckets: the epic id, (multiple epics), (no epic)
tix list --sort epic
tix status --group epic # the same buckets in the board summary
```
`tix epic show` prints the epic's hierarchy, its completion progress, its
weight-aware figure and its blocked members, computed once by the shared service
and read by every surface (CLI, the `F10` **Portfolio** page's Epics tab, and
MCP) so they cannot disagree by value.
**Missing references are never dropped.** Deleting an epic removes only the
registry entry; the issues and child epics that referenced it keep their ids,
`tix lint` reports each as a `missing_epic_reference` naming the holder and the
id (as it does an epic's own project/milestone/cycle/parent references), and the
`F10` page and MCP surface them too. Repair by referencing a live epic or
removing the dangling id (`tix epic remove <id> <issue>`). Deleting an epic
never deletes an issue or a child epic. A relationship a tracker cannot represent
stays local with a diagnostic naming the same relationship (C-007).
The registry reference — field names, optionality, id-never-reused and the
empty-section omission — is in the
[`tix.toml` reference](docs/for-users/configuration.md#epicsepic-id-top-level-optional).
## Roadmap
An **initiative** is a strategic outcome — what the work is *for* — connected to
the projects, epics and milestones that deliver it. The **roadmap** is a read-only
projection that joins those existing services; it introduces no portfolio database,
no second metric and no implicit issue ownership. This is the **documented anchor**
for mission `roadmap-initiatives-01M3C8SX`:
- **The anchor is an `[initiatives.<id>]` registry in `tix.toml`.** Markdown and
configuration remain canonical; the section is omitted when the registry is empty.
- **The id is the identity; the name is a display label.** `tix initiative edit
--name` renames the label only and re-keys nothing, so every association survives.
A deleted id is never reused.
- **Associations are explicit**: a project by **id**, an epic by **id**, a milestone
by **name**. A repeated association collapses to one, and a well-formed reference
that names nothing is stored and stays diagnosable — never inferred from an owner.
- **One filter, one order**: CLI flags, the TUI filter, MCP arguments and the
portable document all parse into the same normalized filter; rows are ordered by
target date ascending, then stable id, and an undated initiative is the explicit
`undated` state that sorts last — never an invented period.
```bash
tix initiative create "Reach 99.9% availability" --owner alice --status active \
--target-date 2026-12-31 --projects p-7b1e04 --epics e-2c9d --milestones 2026-Q4
tix initiative list # id, name, target date, status
tix initiative show i-3f9a2c # the record and its references
tix initiative associate i-3f9a2c --project p-9d1f
tix initiative dereference i-3f9a2c --project p-9d1f
tix initiative delete i-3f9a2c --yes # the registry entry only — nothing else is deleted
tix roadmap list --owner alice --label reliability --from 2026-01-01
tix roadmap show i-3f9a2c
tix export --target roadmap # the versioned portable document
```
`tix roadmap show` prints, for each referenced source entity, its `<kind>:<id>`
provenance, its resolution, the **owning service's own** progress value and the shared
health policy's risk state and policy version — a **join, not a blend**. There is no
aggregate percentage, score or average, and the explicit states are `complete`,
`partial`, `empty`, `missing_reference` and `unknown`. A genuine zero is a value:
`0/0` is `complete`, never confused with "nobody said" (`unknown`) or "nothing is
referenced" (`empty`).
**Missing references are never dropped.** A dangling association stays on the row
(with `[missing]`), in the detail pane and in the ordered diagnostics naming the field
and the reference. `tix lint` reports **issue-held** project/epic references (and an
epic registry's own references); an initiative's references are config-held and are
diagnosed on the roadmap surfaces rather than by lint. The `F10` **Portfolio** page's
Roadmap tab and the MCP tools surface the same values. See the
[roadmap guide](docs/roadmap.md) for the record, the filter semantics, the summary
provenance and the portable document.
## Cycle schedules
A repeating **cycle schedule** declares in `tix.toml` that a cycle starts on a
cadence; `tix` generates each cycle as an ordinary `docs/cycles/*.md` record with a
**stable id** (`sprint:000001`) and a named reference an issue's `cycles` value can
hold. Nothing runs in the background, and nothing is generated without `--confirm`.
```bash
tix cycle schedule create --id sprint --name "Engineering sprint" --cadence weekly \
--interval 2 --duration 2 --duration-unit weeks --start-date 2026-01-05 \
--timezone UTC --look-ahead 2 --confirm
tix cycle schedule list
tix cycle schedule detect # read-only: what is ended or due
tix cycle schedule generate sprint --confirm
tix cycle schedule close sprint --cycle sprint:000001 --reason "shipped" --confirm
```
- **The id is the identity; the name is a display label** — a rename changes nothing
else, and a deleted id is never reused.
- **Detection is read-only and every mutation is confirmed** (`--confirm`). The `F8`
Schedule page's **Cycle schedules** tab drives the same service the CLI does.
- Closing a cycle captures an **immutable snapshot** under `docs/cycle_snapshots/`,
written once and never recomputed, so the evidence survives later schedule edits.
- **A fixed UTC offset only** (`UTC` or `±HH:MM`): a named IANA zone is refused, so a
schedule does not follow daylight saving; `cron-like` cadence is refused by name.
The full reference — every field, the generated cycle, the seven lifecycle actions,
the snapshots and the limits — is in
[`docs/cycle-schedules.md`](docs/cycle-schedules.md).
## Cycle capacity
Capacity is what a cycle, a team or an assignee may commit to, counted in an
estimate unit the board already declares. `tix capacity` configures the ceiling and
reports committed, completed, remaining, overflow and utilization from **one shared
calculation** — the same values the TUI's `F8` **Capacity** tab, the MCP capacity
tools and `tix export --target capacity` report.
```bash
tix capacity set --scope cycle --id sprint:000001 --value 40 --unit d
tix capacity calculate --scope cycle --id sprint:000001 --warnings
tix capacity snapshot sprint:000001
```
- **A scope is a name *or* its stable id** — `sprint:000001` and the instance's own
name are one scope — and it is explicit and visible. Existing units convert only
**explicitly** (`1w` = `5d` under the board's declared ratio); a missing or
incompatible unit is an actionable diagnostic, never a silent zero, and an issue
with no estimate stays visible as unestimated.
- **Overflow is a report, not a mutation**: a shared warning naming the scope, the
committed value, the ceiling, the excess and the unit. Reading a capacity surface
writes nothing.
- **Closing a cycle captures immutable capacity evidence** under
`docs/cycle_snapshots/`, written once and never recomputed; a reopen appends one
History event and leaves the prior snapshot byte-identical.
The full reference — the configuration shape, the formulas, the warning vocabulary,
the detail views, the close snapshots and the portable/remote boundary — is in
[`docs/capacity.md`](docs/capacity.md).
## Time tracking
Time tracking records **actual effort** against an issue: a whole number of minutes,
an optional `YYYY-MM-DD` calendar date, an author and an optional note. There is no
timer — no start/stop, no clock read, no timezone conversion and no overlap rule —
and current entries live in the issue's own frontmatter (`time_entries`), never in a
database or a hosted service.
```bash
tix time add 42 --minutes 90 --date 2026-03-01 --author ada --note "parser review"
tix time edit 42 --entry e-1a2b3c4d --minutes 120 --reason "recount"
tix time delete 42 --entry e-1a2b3c4d --reason "wrong issue" --confirm
tix time show 42 # the issue's current entries and its total
tix time history 42 # the audit view: current beside what History keeps
tix time total --assignee ada --from 2026-03-01 --to 2026-03-31 --by issue
```
- **Every mutation is one attributable History event**, naming the entry and its
before/after values; a correction and a deletion each keep the prior value, so the
audit trail is never rewritten. A refused mutation (an unconfirmed delete, a
missing reason, an unknown entry, a blank author) writes **nothing**.
- **An absent value is `-`, never `0`.** A scope that selected no entry (`nothing`)
and a scope that selected entries with no usable duration (`unknown`) are different
facts from a recorded `0`, and a malformed entry is reported with its kind and its
remedy without blocking any other read.
- **One shared calculation** feeds every surface: the CLI, the TUI's `F7` Stats
page's **Time** tab and the issue detail pane, the six MCP time tools, and
`tix export --target time` — so no surface can print a number another would not.
- Time entries are **not** estimates: the issue's free-form `estimate` is never
converted, and no time operation changes an issue's status or ownership.
The full reference — the entry shape, the store representation, the four surfaces,
the never-zero rule and the escalation boundaries — is in
[`docs/time-tracking.md`](docs/time-tracking.md).
## Batch editing
`tix bulk edit|link|unlink` over a selection: preview is the default, `--apply`
writes, and each item reports its own outcome with no rollback. See
[batch editing](docs/for-users/batch-editing.md).
## Archiving
`tix archive`/`tix restore` dot-prefix an issue's filename to hide it from the
default views without deleting it; the number is never recycled. See
[archiving](docs/for-users/archiving.md).
## Saved issue views
A saved view names a filter/sort/group/visibility definition so it can be reused
from the CLI, the TUI, and MCP. Views live in `tix.toml` under
`[views.<view-id>]` — there is no database and no hosted view service.
```bash
tix view create --name "Active work" --filter "status:todo" --sort due
tix view list # id, name, scope, resolution
tix view apply v-1f0c9a3d5b7e2411 # read-only; prints the plan and the result
tix view status v-1f0c9a3d5b7e2411 # how the live board diverged from the saved plan
```
- The `v-…` **id is the identity**; the name is a display label, so renaming a
view changes nothing else and a deleted id is never reused.
- Plans use the existing query grammar only (`--filter`, `--sort`, `--group`,
`--hide-columns`, `--show-mismatch`) — no second query language, and
unsupported fields are refused before anything is written.
- Scope (`repository`, `project`, `user`) is explicit. `project` outranks
`repository`; a name shared by two equally specific views is a
`scope_conflict`, never an implicit pick. `user` scope is share metadata only —
not an account and not a permission.
- Applying a view is **read-only** and **informational**: it marks the active
view and reports divergence; it never silently overwrites the live filters and
never touches an issue.
- `tix view delete <id> --confirm` removes **only the saved definition** — never
an issue, comment, or relationship.
- Unknown metadata round-trips verbatim, and the same normalized plan/result
feeds the CLI, TUI, MCP, and portable output.
The full reference — scope precedence, divergence, deletion safety, portable
format, and the escalation boundaries — is in
[`docs/saved-views.md`](docs/saved-views.md).
## Workflow status categories
Map board columns onto the global six-word vocabulary (`backlog` … `canceled`).
Declaring a `category_map` opts the board into the transition policy; an unmapped
column falls back to `unstarted`. See
[workflow status categories](docs/for-users/workflow-categories.md).
## Triage inbox
An opt-in inbox, declared by a `[triage]` section in `tix.toml`, holds unreviewed
work before it joins the normal workflow. Every decision is one History event,
and the CLI, the TUI (**F2**) and MCP read one snapshot through the same service.
See [triage](docs/for-users/triage.md) and the design reference
[`docs/triage.md`](docs/triage.md).
## Templates
Reusable issue/form definitions live in `tix.toml` under `[issue_templates.<id>]`,
keyed by an **immutable stable ID** with a mutable display name, an explicit
repository/board/team scope, normal create **defaults**, ordered data-only
**prompts**, and typed **references**; unknown metadata is preserved across round
trips. `tix template list|show|create|update|duplicate|delete` manages them, and
`tix create --template <id> [--scope …]` merges a template with the normal create
flags through a pure resolver before calling the same create core as any other
create — there is no second issue writer, no hosted service, and no executable
template code. Required prompts and missing references fail **before** any file
is written, so a failed form never creates a partial issue. See
[templates](docs/templates.md).
## External sync
`tix export/import` targets GitLab (through `glab`) and Linear (through
[`linear-cli`](https://crates.io/crates/linear-cli) — the canonical
nesszer/linear-cli crate, pinned in `mise.toml`), plus a local
`tix import --target speckit`. Repeated runs are idempotent, and a blocking
relation exports to Linear's `relations add`. `tix export --target beads` writes
local issues into a [beads](https://github.com/gastownhall/beads) workspace
**through the `bd` CLI** (`bd import`), never its Dolt database. See
[external sync](docs/for-users/external-sync.md).
## Configuration
`tix.toml` holds the issues directory, `[export]` targets and legacy tables;
sparse TUI overrides live in optional `tix_keybindings.toml`; boards live in
`docs/boards/<name>.md`. The full key reference is in
[configuration](docs/for-users/configuration.md), the TUI editing surface in
[config mode](docs/for-users/config.md), and the metadata stores in
[navigation](docs/for-users/navigation.md#project-metadata-stores).
## Documentation
Beyond the guides linked above, `docs/for-users/` also holds
[navigation](docs/for-users/navigation.md) (mode shell and metadata stores),
[new issue](docs/for-users/new-issue.md), [cycles](docs/for-users/cycles.md)
and [config mode](docs/for-users/config.md), plus
[key bindings](docs/key-bindings.md) and [MCP](docs/mcp.md). The stable-ID
template registry is documented in [templates](docs/templates.md), and the
recurring series surface — rules, identity, the three surfaces, idempotency and
its escalation boundaries — in [recurring](docs/recurring.md), and the
work-in-progress limits — the TOML shape, the estimate units, the override rules
and the boundaries — in [wip](docs/wip.md), and the repeating cycle schedules —
the TOML shape, the generated cycles, the lifecycle actions and the
snapshot/offset limits — in [cycle schedules](docs/cycle-schedules.md), and cycle
capacity — the scopes, the explicit conversions, the formulas, the close snapshots
and the portable/remote boundary — in [capacity](docs/capacity.md), and per-issue
time tracking — the entry shape, the four surfaces, the never-zero rule and the
boundaries — in [time tracking](docs/time-tracking.md).
## Version
Current version: **0.12.0**. See [CHANGELOG.md](CHANGELOG.md).
## License
MIT OR Apache-2.0.