Skip to main content

Config

Struct Config 

Source
pub struct Config {
Show 35 fields pub kanban: KanbanSection, pub issues_dir: PathBuf, pub milestones_dir: Option<PathBuf>, pub teams: IndexMap<String, Team>, pub cycles: IndexMap<String, PlanningCycle>, pub templates: IndexMap<String, IssueTemplate>, pub views: ViewRegistry, pub health: HealthConfig, pub issue_templates: TemplateRegistry, pub planning: PlanningConfig, pub categories: CategoryConfig, pub transitions: TransitionConfig, pub triage: Option<TriageConfig>, pub editor: Option<String>, pub viewer: Option<String>, pub agent: Option<String>, pub state: StateConfig, pub export: ExportConfig, pub custom_attributes: CustomAttributes, pub lint_foreign_repositories: bool, pub tips_on_startup: bool, pub mentions: MentionsConfig, pub layout: LayoutConfig, pub types: Option<Vec<String>>, pub labels: Option<Vec<String>>, pub type_colors: IndexMap<String, String>, pub tags_dir: Option<PathBuf>, pub repos: IndexMap<String, PathBuf>, pub parent_repo: Option<PathBuf>, pub projects: IndexMap<String, ProjectConfiguration>, pub epics: IndexMap<String, EpicConfiguration>, pub initiatives: IndexMap<String, InitiativeConfiguration>, pub cycle_schedules: IndexMap<String, CycleScheduleConfiguration>, pub capacity: CapacityConfig, pub beads: BeadsConfig,
}
Expand description

The project configuration file (tix.toml).

Not Eq, unlike each of the sections it holds: Config::views preserves unknown view metadata as toml::Value, whose float variant includes nan, so the reflexive equality Eq promises cannot be guaranteed for a config that carries one. PartialEq is what every comparison in the tree needs — save then load round-trip tests included.

Fields§

§kanban: KanbanSection

Named board configurations ([kanban]). Always ≥1 after load.

§issues_dir: PathBuf

Directory holding issue files, relative to the repo root.

§milestones_dir: Option<PathBuf>

Directory holding milestone markdown files, relative to the repo root. None → the default docs/milestones.

§teams: IndexMap<String, Team>

Named teams ([teams.<label>]), in declaration order.

§cycles: IndexMap<String, PlanningCycle>

Named cycles ([cycles.<name>]), in declaration order.

§templates: IndexMap<String, IssueTemplate>

Named new-issue templates ([templates.<name>]), in declaration order.

Optional and absent by default: a project with no templates writes no section, and the F4 New Issue mode simply offers none.

§views: ViewRegistry

Saved view definitions ([views.<view-id>]), in declaration order.

Mission saved-issue-views-01M3C926 (WP01). Omitted when empty, so a repository that has never saved a view gains no key on save. The records are crate::views::registry::SavedViews: a stable id, a mutable display name, explicit scope metadata, the query plan, and preserved extension fields. Every write goes through crate::views::registry::ViewRegistry::commit.

§health: HealthConfig

Health policy configuration ([health]). Omitted when absent.

§issue_templates: TemplateRegistry

Stable-ID issue/form templates ([issue_templates.<id>]), in declaration order.

As distinct from Self::templates as an ID is from a name: this is the registry behind tix template and tix create --template, keyed by an immutable ID with an explicit repository/board/team scope, typed defaults and prompts, typed references, and preserved unknown metadata (crate::templates::registry). The legacy name-keyed templates above only pre-fills the TUI New Issue form. Omitted when empty, so a project with no stable-ID templates gains no section and a legacy file round-trips byte-identically.

§planning: PlanningConfig

Planning presets and the auto-cycle counter ([planning]).

§categories: CategoryConfig

[categories]: the global vocabulary and safe default. Omitted when untouched, so a legacy file gains no key on save.

§transitions: TransitionConfig

[transitions]: the workflow policy. Omitted when untouched.

§triage: Option<TriageConfig>

[triage]: the normal columns that make up the triage inbox.

Mission triage-inbox-review-flow-01M3C600. The inbox is opt-in: None means no section, so no inbox, and every tix triage command refuses by naming the missing section. tix init writes one, with the board columns it needs, so a new repository starts with the inbox on; a legacy tix.toml gains no key on save. See crate::triage::config.

§editor: Option<String>

Optional $EDITOR override for full-file editing.

§viewer: Option<String>

Read-only viewer for the TUI’s v key (tix #1507).

None and Some("auto") both mean the built-in probe chain — mdterm, then batcat, most, less, more — and every other value names the program to run directly. Never written when unset, so a legacy file gains no key on save.

§agent: Option<String>

Configured AI agent command (opencode, claude, etc.) launched with the issue body as its specification. Mirrors the editor resolution chain (explicit → $TIX_AGENT → $AGENT env → PATH fallback).

§state: StateConfig

Persisted runtime state ([state]). Tolerated on load for legacy files, never written back to disk (issue #52).

§export: ExportConfig

External tracker targets ([export]). Omitted when empty, so a legacy file gains no section on save (#1415).

§custom_attributes: CustomAttributes

Top-level frontmatter keys this project uses on purpose ([custom_attributes]).

#1361. tix preserves a frontmatter key it does not recognise and warns about it, because an unrecognised key is usually a typo (titel:) and a typo that round-trips silently is worse than one that is dropped. A project that does use a custom key declares it here once and stops being warned.

Top-level names only, not dotted paths — the thing being declared is a top-level key, and accepting a path syntax here would imply nested declarations are checked, which they are not.

§lint_foreign_repositories: bool

Whether tix lint also lints the foreign repositories reachable from this store’s related_issues path references and merges their warnings.

Default true. Skipped when true so a tix.toml that does not set it round-trips byte-identically; only an explicit false is written.

§tips_on_startup: bool

Whether the TUI opens its “did you know?” tips modal on startup (tix #1632).

Default true: a session that reaches the TUI shows one tip, then gets out of the way. tix init writes the key explicitly, so the setting is discoverable where the rest of the configuration lives, and the modal’s d key writes false back. Skipped when true for the same reason Self::lint_foreign_repositories is, so a file that does not mention it round-trips byte-identically.

§mentions: MentionsConfig

[mentions]: durable @handle / @email mention tracking.

OpenSpec change issue-mentions. Absent means the defaults — enabled, under docs/mentions — so no legacy file gains a key on save.

§layout: LayoutConfig

[layout]: configurable on-disk issue layout.

§types: Option<Vec<String>>

Allowed issue type: values (tix #1530 renamed the key from custom_types; the old name is still accepted on read).

tix init writes the default set (see DEFAULT_TYPES); tix lint accepts an issue’s type: only from this list. None falls back to DEFAULT_TYPES, so a repository that never configured it still gets a meaningful check.

§labels: Option<Vec<String>>

Extra known issue labels for this project, beyond the builtins.

BUILTIN_LABELS are always resolved (see Self::labels), so this key is an optional extension: a project that wants to name its own additional labels lists them here. tix init does not write the key — a builtin needs no key, and emitting a brand-new key would make a fresh tix.toml unreadable to a released binary (deny_unknown_fields). Omitted when unset, so a legacy file gains no key on save.

Unlike Self::types, tix lint does not reject an undeclared label: labels are free-form across the ecosystem (tracker mirrors, import provenance), and a hard allow-list would flag every one of them.

§type_colors: IndexMap<String, String>

Per-type accent colors ([type_colors], tix #1530).

Maps a type name to a color name. A type with no entry uses the shipped DEFAULT_TYPE_COLORS. Omitted when empty, so an untouched file gains no table on save.

§tags_dir: Option<PathBuf>

Directory holding tag definition files (tags_dir, tix #1530).

Each *.md there names a tag and its color:; a tag colors the issue label of the same name in the TUI and the CLI. None → the default docs/tags. Absent by default, so a file gains no key on save.

§repos: IndexMap<String, PathBuf>

Child repositories this project coordinates ([repos], tix #1529).

A parent-only map: a project-management repository names the repositories it manages, one level deep, and each value is that child’s project root (the directory holding its tix.toml), relative to this file. Absent by default, so a file gains no section on save. The TUI’s Repos mode lists these and runs tix inside the chosen one.

§parent_repo: Option<PathBuf>

The parent repository, when this project is itself a managed child (tix #1529).

Set here, the TUI’s Repos mode offers a limb that runs tix in the parent. The parent also sets TIX_PARENT_REPO when it spawns a child, so the jump back works even without this key.

§projects: IndexMap<String, ProjectConfiguration>

Project records ([projects.<project-id>]), in declaration order.

Mission first-class-projects-01M3BYBW (WP01). The map key is a stable generated ProjectId and never a display name (C-002), so renaming a project leaves every issue association and derived figure intact. Omitted entirely when empty, so a project-free repository gains no section on save and a legacy tix.toml round-trips byte-identically (C-001). Issue-side associations are the projects list in issue frontmatter; this registry is the only place a project’s own record lives.

§epics: IndexMap<String, EpicConfiguration>

Epic records ([epics.<epic-id>]), in declaration order.

Mission epics-work-items-01M3BYW4 (WP01). The registry key is a stable generated EpicId and never a display name (C-003), so renaming an epic leaves every member issue’s reference and every derived figure intact. Omitted entirely when empty, so an epic-free repository gains no section on save and a legacy tix.toml round-trips byte-identically (C-004). Member issues point at an epic through the epics list in their frontmatter; this registry is the only place an epic’s own record lives. Reference fields (parent, projects, milestone, cycle) are shape-checked only — resolution and diagnosis belong to the epic service and tix lint (C-005).

§initiatives: IndexMap<String, InitiativeConfiguration>

Initiative records ([initiatives.<initiative-id>]), in declaration order.

Mission roadmap-initiatives-01M3C8SX (WP01). The registry key is a stable generated InitiativeId and never a display name (C-002), so renaming an initiative leaves every association and derived figure intact. Omitted entirely when empty, so an initiative-free repository gains no section on save and a legacy tix.toml round-trips byte-identically (C-001). All writes go through the atomic boundary in crate::roadmap::registry, which validates the whole change before Config::save touches the file; reference fields (projects, epics, milestones) are shape-checked only — resolution and diagnosis belong to the initiative service (WP03) and tix lint (C-005).

Unlike its two siblings this record preserves unknown metadata rather than refusing it; see InitiativeConfiguration.

§cycle_schedules: IndexMap<String, CycleScheduleConfiguration>

Cycle-schedule definitions ([cycle_schedules.<schedule-id>]), in declaration order.

Mission repeating-cycle-schedules-01M3C7W1 (WP01). A schedule is not a cycle: the legacy, name-keyed [cycles.<name>] registry above stays exactly as it is, and issue cycles references keep pointing at it (C-002). A schedule is the stable-ID definition — cadence, duration, start date, timezone, look-ahead, state — from which cycles are explicitly generated (WP04).

The registry key is a stable generated crate::cycle_schedule::config::CycleScheduleId and never the display name (C-003), so a rename leaves every generated cycle, cursor and closed snapshot intact. Omitted entirely when empty, so a schedule-free repository gains no section on save and a legacy tix.toml round-trips byte-identically (C-001). Every field is validated by crate::cycle_schedule::config::validate_schedules before persistence, with a diagnostic that names the field (C-005).

§capacity: CapacityConfig

Capacity ceilings ([capacity.<entry-id>]), in declaration order.

Mission cycle-capacity-planning-01M3C85C (WP01). Each entry names exactly one scope — a cycle, a team, or an assignee — and the ceiling it is allowed to commit to, in an estimate unit the WIP registry already declares (C-003, C-004). The entry’s key is only a handle: the scope is a field of the record, so “which dimension is this?” is always stated rather than inferred from a table’s shape, and crate::capacity::config::validate refuses an entry that names none or more than one.

Omitted entirely when empty, so a capacity-free repository gains no section on save and a legacy tix.toml round-trips byte-identically (C-001), and unknown metadata inside an entry is preserved verbatim for a newer tix. A closed cycle’s capacity is historical evidence: this section holds no snapshot state, and crate::capacity::config::ensure_cycle_editable is the seam that refuses a configuration edit aimed at a closed cycle (C-008).

§beads: BeadsConfig

How this repository talks to a beads workspace ([beads]).

Mission-tracking issues tix #1709/#1710. off (the default) writes no section, so a repository that never opts in is unchanged and round-trips byte-identically. mirror reads a beads workspace through the bd CLI without writing anything anywhere; sync keeps both sides in step through two one-way pipes. Every interaction is a bd CLI call — tix never opens the Dolt database. See crate::sync::beads.

Implementations§

Source§

impl Config

Source

pub fn load_with_warning(path: &Path, sink: impl FnMut(&str)) -> Result<Config>

Load a tix.toml at path, emitting a warning through sink for every cleartext token read from the file.

Parse and validation failures surface as TixError::Config with the file’s path and (where the TOML parser reports it) line.

Source

pub fn load(project_root: &Path) -> Result<Config>

Load the config for a project root: tix.toml at the root if present, otherwise the defaults. An absent file is not an error — the CLI layer decides whether to nag about tix init.

Source

pub fn project_root(cwd: &Path) -> Option<PathBuf>

Find the project root for cwd by walking upward for a tix.toml. Returns None when no ancestor (including cwd itself) holds one.

Source

pub fn save(&self, path: &Path) -> Result<()>

Write tix.toml atomically (temp file + rename in the same directory, with an fsync before the rename — NFR-006).

Tokens are never written to disk, so a round-trip load → save drops any cleartext token (expected: prefer env vars).

Source§

impl Config

Source

pub fn starter() -> Config

The configuration a new repository starts with — what tix init writes.

Deliberately not Default: that is the fallback for a missing or partial tix.toml (and the base hundreds of tests build fixtures on), and it stays minimal. The starting point is a product decision: a new repository gets the board starter_board lays out and a working [triage] section, so tix triage list works on a fresh checkout instead of refusing and asking for configuration first.

The three keys match the board: unreviewed work sits in triage, an accept lands in todo, a decline in wontfix. deferred_status is left unset because it defaults to entry_status, and a default that is never written is a default that cannot drift from the meaning it stands for.

Source

pub fn allocate_project_id(&self) -> ProjectId

Allocate a ProjectId that is not already in the registry.

Redraws against the stored ids, so a stored id is never handed out a second time. The rest of the never-reused guarantee is carried by the id shape itself (see StableId::generate); there is no counter persisted in tix.toml, so a deletion cannot reset an allocator — the id a deleted project carried stays spent because no draw can repeat one this process already produced.

Source

pub fn allocate_epic_id(&self) -> EpicId

Allocate an EpicId that is not already in the registry.

Mirrors Self::allocate_project_id: the draw is redrawn against the stored ids, so a stored id is never handed out a second time, and the rest of the never-reused guarantee is carried by the id shape itself (StableId::generate) — a deleted epic’s id stays spent, keeping a member issue’s dangling reference diagnosable rather than silently rebound (C-005).

Source

pub fn allocate_initiative_id(&self) -> InitiativeId

Allocate an InitiativeId that is not already in the registry.

Mirrors Self::allocate_project_id: the draw is redrawn against the stored ids, so a stored id is never handed out a second time, and the rest of the never-reused guarantee is carried by the id shape itself (StableId::generate) — a deleted initiative’s id stays spent, keeping the record’s identity distinct from any later one.

Source

pub fn allocate_cycle_schedule_id(&self) -> CycleScheduleId

Allocate a fresh crate::cycle_schedule::config::CycleScheduleId.

Mirrors Self::allocate_initiative_id: the draw is redrawn against the stored ids, so a stored id is never handed out a second time, and the rest of the never-reused guarantee is carried by the id shape itself (StableId::generate) — a deleted schedule’s id stays spent, keeping the record’s identity distinct from any later one (C-003).

Source

pub fn custom_attributes(&self) -> Vec<String>

Declared custom top-level frontmatter keys, lowercased for comparison.

Lowercased because YAML keys are case-sensitive but nobody has ever intended Titel and titel to be different keys, and a suppression list that silently fails to match is worse than no list at all.

Source

pub fn types(&self) -> Vec<String>

The allowed issue types: the configured types, or DEFAULT_TYPES when unset. Compared case-insensitively by callers.

Source

pub fn custom_types(&self) -> Vec<String>

The pre-#1530 name for Self::types, kept so existing call sites and tests read the same list under the old spelling.

Source

pub fn labels(&self) -> Vec<String>

The recognised issue labels: BUILTIN_LABELS, plus any extra labels the project declares under labels in tix.toml.

Builtins come first and are always present, so decision-pending resolves in a repository that never configured the key — which is why tix init does not write one.

Source

pub fn type_color(&self, type_name: &str) -> Option<String>

The color name for issue type, from [type_colors] when set for it, else the shipped DEFAULT_TYPE_COLORS entry. None for a type with no color anywhere (an unknown type). Compared case-insensitively (tix #1530).

Source

pub fn attributes(&self) -> Vec<String>

The attribute list shown and edited in the Config view: the declared [custom_attributes].keys, or the built-in attributes tix models when none are declared. Mirrors Self::custom_types (configured-or-defaults).

Source

pub fn milestones_dir(&self) -> &Path

The resolved milestones directory: the configured value, or the contract default docs/milestones when unset.

Source

pub fn tags_dir(&self) -> &Path

The resolved tags directory: the configured tags_dir, or the contract default docs/tags when unset (tix #1530).

Source

pub fn repos(&self) -> &IndexMap<String, PathBuf>

The child repositories this project coordinates ([repos], tix #1529).

Source

pub fn parent_repo(&self) -> Option<&Path>

The parent repository, when this project is a managed child (tix #1529).

Source

pub fn resolve_repo_paths(&mut self, base: &Path)

Absolutize the paths that are resolved against the project root: an explicitly configured tags_dir, every [repos] entry, and parent_repo (tix #1529/#1530).

Only a relative configured value is rewritten, so an unset key stays None (and is not materialized on save) and an absolute one is left alone. issues_dir/milestones_dir are resolved elsewhere; this covers the newer keys so the TUI can spawn tix in a child from any working directory.

Source

pub fn default_config(&self) -> &BoardConfiguration

The configuration used by the CLI and other single-config surfaces: the one named default if present, else the first declared.

Source

pub fn configs(&self) -> impl Iterator<Item = (&str, &BoardConfiguration)>

Named configurations in declaration order (tab order = declaration order, D-02).

Trait Implementations§

Source§

impl Clone for Config

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Config

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Config

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for Config
where Config: Default,

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl PartialEq for Config

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for Config

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Config

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.