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
#[allow(clippy::wildcard_imports)]
use super::*;
/// The `tix initiative` subcommands (mission `roadmap-initiatives-01M3C8SX`, WP04).
///
/// A **required** subcommand, like `tix project` and `tix epic`. Selection is by the
/// stable id (the registry key); a name resolves to the same initiative as a
/// convenience. A rename through `edit --name` changes the label only: the id is not
/// re-keyed, so every association and derived value survives.
#[derive(Subcommand)]
pub enum InitiativeCommand {
/// Create an initiative and allocate a stable id
///
/// The name is a display label, not the identity: `tix initiative edit --name`
/// changes it and every association survives, because the record is keyed by id.
Create {
/// Display name (unique within the registry, case-insensitively)
#[arg(value_name = "NAME")]
name: String,
/// Optional strategic outcome
#[arg(long)]
outcome: Option<String>,
/// Owner identity string; no lookup, never inferred
#[arg(long)]
owner: Option<String>,
/// Free-form initiative status (not a board column)
#[arg(long)]
status: Option<String>,
/// Initiative priority (a known spelling is normalized; an unknown one is kept)
#[arg(long)]
priority: Option<String>,
/// Filterable labels, comma-separated
#[arg(long)]
labels: Option<String>,
/// Target date (`YYYY-MM-DD`)
#[arg(long = "target-date")]
target_date: Option<String>,
/// Project ids this initiative concerns, comma-separated
///
/// Shape-checked only, never resolved: a well-formed id the registry does not
/// hold is stored and diagnosed by `tix roadmap`/`tix lint`, so it is
/// repairable.
#[arg(long, value_name = "ID")]
projects: Option<String>,
/// Epic ids this initiative concerns, comma-separated
#[arg(long, value_name = "ID")]
epics: Option<String>,
/// Milestone names this initiative concerns, comma-separated
#[arg(long, value_name = "NAME")]
milestones: Option<String>,
},
/// Edit an initiative's fields; a rename changes the label only
///
/// Nothing is re-keyed, so every association and derived value stays intact. An
/// optional value with a blank spelling clears the field (`--owner ""`), and a
/// blank reference list clears it (`--projects ""`).
Edit {
/// Initiative id (a name is accepted as a convenience)
#[arg(value_name = "ID")]
reference: String,
/// New display name
#[arg(long)]
name: Option<String>,
/// New strategic outcome (`--outcome ""` clears)
#[arg(long)]
outcome: Option<String>,
/// New owner identity string (`--owner ""` clears)
#[arg(long)]
owner: Option<String>,
/// New free-form status (`--status ""` clears)
#[arg(long)]
status: Option<String>,
/// New priority (`--priority ""` clears)
#[arg(long)]
priority: Option<String>,
/// Replace the label list, comma-separated (`--labels ""` clears)
#[arg(long)]
labels: Option<String>,
/// New target date (`YYYY-MM-DD`; `--target-date ""` clears)
#[arg(long = "target-date")]
target_date: Option<String>,
/// Replace the project ids, comma-separated (`--projects ""` clears)
#[arg(long, value_name = "ID")]
projects: Option<String>,
/// Replace the epic ids, comma-separated (`--epics ""` clears)
#[arg(long, value_name = "ID")]
epics: Option<String>,
/// Replace the milestone names, comma-separated (`--milestones ""` clears)
#[arg(long, value_name = "NAME")]
milestones: Option<String>,
},
/// List initiatives in declaration order, with ids, target dates and status
List,
/// Show one initiative's record, metadata and references
Show {
/// Initiative id (a name is accepted as a convenience)
#[arg(value_name = "ID")]
reference: String,
},
/// Delete an initiative's registry entry (nothing else is ever deleted)
///
/// The record's own references are reported and nothing cascades: no project,
/// epic, milestone or issue is touched, and no association is silently repaired.
Delete {
/// Initiative id (a name is accepted as a convenience)
#[arg(value_name = "ID")]
reference: String,
/// Confirm without prompting
#[arg(long)]
yes: bool,
},
/// Associate an initiative with exactly one project, epic or milestone
///
/// Name exactly one of `--project`, `--epic`, `--milestone`; the kind is part of
/// the record, so guessing is worse than asking. A well-formed reference that
/// names nothing is stored and stays diagnosable, and a repeated association is
/// an idempotent no-op that writes no bytes.
Associate {
/// Initiative id (a name is accepted as a convenience)
#[arg(value_name = "ID")]
reference: String,
/// Project **id** to associate
#[arg(long, value_name = "ID")]
project: Option<String>,
/// Epic **id** to associate
#[arg(long, value_name = "ID")]
epic: Option<String>,
/// Milestone **name** to associate
#[arg(long, value_name = "NAME")]
milestone: Option<String>,
},
/// Drop an explicit association from an initiative
///
/// Name exactly one of `--project`, `--epic`, `--milestone`. Dropping an
/// association that is not recorded is an idempotent no-op that writes no bytes.
Dereference {
/// Initiative id (a name is accepted as a convenience)
#[arg(value_name = "ID")]
reference: String,
/// Project **id** to drop
#[arg(long, value_name = "ID")]
project: Option<String>,
/// Epic **id** to drop
#[arg(long, value_name = "ID")]
epic: Option<String>,
/// Milestone **name** to drop
#[arg(long, value_name = "NAME")]
milestone: Option<String>,
},
}