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: KanbanSectionNamed board configurations ([kanban]). Always ≥1 after load.
issues_dir: PathBufDirectory 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: ViewRegistrySaved 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: HealthConfigHealth policy configuration ([health]). Omitted when absent.
issue_templates: TemplateRegistryStable-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: PlanningConfigPlanning 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: StateConfigPersisted runtime state ([state]). Tolerated on load for legacy
files, never written back to disk (issue #52).
export: ExportConfigExternal tracker targets ([export]). Omitted when empty, so a legacy
file gains no section on save (#1415).
custom_attributes: CustomAttributesTop-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: boolWhether 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: boolWhether 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.
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: CapacityConfigCapacity 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: BeadsConfigHow 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
impl Config
Sourcepub fn load_with_warning(path: &Path, sink: impl FnMut(&str)) -> Result<Config>
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.
Sourcepub fn load(project_root: &Path) -> Result<Config>
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.
Sourcepub fn project_root(cwd: &Path) -> Option<PathBuf>
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§impl Config
impl Config
Sourcepub fn starter() -> Config
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.
Sourcepub fn allocate_project_id(&self) -> ProjectId
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.
Sourcepub fn allocate_epic_id(&self) -> EpicId
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).
Sourcepub fn allocate_initiative_id(&self) -> InitiativeId
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.
Sourcepub fn allocate_cycle_schedule_id(&self) -> CycleScheduleId
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).
Sourcepub fn custom_attributes(&self) -> Vec<String>
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.
Sourcepub fn types(&self) -> Vec<String>
pub fn types(&self) -> Vec<String>
The allowed issue types: the configured types, or DEFAULT_TYPES
when unset. Compared case-insensitively by callers.
Sourcepub fn custom_types(&self) -> Vec<String>
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.
Sourcepub fn labels(&self) -> Vec<String>
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.
Sourcepub fn type_color(&self, type_name: &str) -> Option<String>
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).
Sourcepub fn attributes(&self) -> Vec<String>
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).
Sourcepub fn milestones_dir(&self) -> &Path
pub fn milestones_dir(&self) -> &Path
The resolved milestones directory: the configured value, or the
contract default docs/milestones when unset.
The resolved tags directory: the configured tags_dir, or the contract
default docs/tags when unset (tix #1530).
Sourcepub fn repos(&self) -> &IndexMap<String, PathBuf>
pub fn repos(&self) -> &IndexMap<String, PathBuf>
The child repositories this project coordinates ([repos], tix #1529).
Sourcepub fn parent_repo(&self) -> Option<&Path>
pub fn parent_repo(&self) -> Option<&Path>
The parent repository, when this project is a managed child (tix #1529).
Sourcepub fn resolve_repo_paths(&mut self, base: &Path)
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.
Sourcepub fn default_config(&self) -> &BoardConfiguration
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.
Sourcepub fn configs(&self) -> impl Iterator<Item = (&str, &BoardConfiguration)>
pub fn configs(&self) -> impl Iterator<Item = (&str, &BoardConfiguration)>
Named configurations in declaration order (tab order = declaration order, D-02).
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Config
impl<'de> Deserialize<'de> for Config
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl StructuralPartialEq for Config
Auto Trait Implementations§
impl Freeze for Config
impl RefUnwindSafe for Config
impl Send for Config
impl Sync for Config
impl Unpin for Config
impl UnsafeUnpin for Config
impl UnwindSafe for Config
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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