tix-tracker 0.11.0

A terminal issue tracker: markdown + YAML frontmatter issues with a kanban TUI and CLI
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
# 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.

![tix — a kanban TUI over plain Markdown issues](https://gitlab.com/thyseus/tix/-/raw/main/docs/assets/screenshot.png)

> **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

| Command | What it does |
| --- | --- |
| `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 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]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 (`tips.toml` at the root of this
repository); `←`/`→` 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

| Mode | Key | What it shows | Guide |
| --- | --- | --- | --- |
| 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, keybindings and legacy
tables; 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.11.0**. See [CHANGELOG.md](CHANGELOG.md).

## License

MIT OR Apache-2.0.