tix/cli/cli_health.rs
1#[allow(clippy::wildcard_imports)]
2use super::*;
3
4/// The `tix triage` tree (`contracts/cli-surface.md`, WP05 T023–T025).
5///
6/// `list` and `status` are read-only queue views; the four decision verbs take a
7/// single issue; `batch` is the selection-shaped twin of `tix bulk`, preview by
8/// default and writing only under an explicit `--apply`.
9///
10/// ## Why the reason flag is `Option` on `accept` and the rest
11///
12/// `contracts/decision-history.md` splits it: decline, duplicate and defer
13/// *require* a reason, accept records one when supplied. A required clap argument
14/// on three of the four would make the fourth's absence look like an oversight,
15/// and — more importantly — the enforcement belongs to the service, which refuses
16/// `missing_reason` before any write on every surface (NFR-003). The flag is
17/// therefore optional everywhere and the *rule* is the service's, so the CLI,
18/// TUI and MCP cannot end up with three different ideas of which decision needs
19/// one. `tix triage decline 1` with no `--reason` still fails, at the service,
20/// with the contract's own kind.
21/// `tix health`'s actions.
22#[derive(Subcommand)]
23pub enum HealthAction {
24 /// Record an issue's health state (append-only; needs --rationale)
25 ///
26 /// Writes the frontmatter value and **one** History entry, together or not at
27 /// all. Any of the four states is accepted; anything else is refused by name.
28 Set {
29 /// The issue, by number or slug
30 issue: String,
31 /// One of `unknown`, `on_track`, `needs_attention`, `at_risk`
32 #[arg(long)]
33 state: String,
34 /// Who is making the change (defaults to the configured git identity)
35 #[arg(long)]
36 actor: Option<String>,
37 /// When, as `YYYY-MM-DD` (defaults to today)
38 #[arg(long)]
39 date: Option<String>,
40 /// Why — required: a health change nobody can explain is not recorded
41 #[arg(long)]
42 rationale: Option<String>,
43 },
44 /// Show one issue's health: its own value, its derived rollup, its diagnostics
45 Show {
46 /// The issue, by number or slug
47 issue: String,
48 },
49 /// Show a scope's derived health, with its contributors and policy version
50 Rollup {
51 /// `issue` | `project` | `epic` | `cycle` | `milestone`
52 #[arg(long)]
53 scope: String,
54 /// The scope's stable id (an issue number for `--scope issue`)
55 #[arg(long)]
56 id: String,
57 },
58 /// Layer an explicit override over a derived scope's health
59 ///
60 /// An override is a deliberate act about a whole scope, so it needs `--confirm`:
61 /// it does not replace what the children say, it is recorded *over* it.
62 Override {
63 /// `project` | `epic` | `cycle` | `milestone`
64 #[arg(long)]
65 scope: String,
66 /// The scope's stable id
67 #[arg(long)]
68 id: String,
69 /// One of `unknown`, `on_track`, `needs_attention`, `at_risk`
70 #[arg(long)]
71 state: String,
72 /// Who is making the change (defaults to the configured git identity)
73 #[arg(long)]
74 actor: Option<String>,
75 /// When, as `YYYY-MM-DD` (defaults to today)
76 #[arg(long)]
77 date: Option<String>,
78 /// Why — required
79 #[arg(long)]
80 rationale: Option<String>,
81 /// Confirm an override of derived health
82 #[arg(long)]
83 confirm: bool,
84 },
85 /// The append-only health History entries about a scope
86 History {
87 /// A scope: `issue:42`, `cycle:2026-10`, `milestone:m1`, …
88 scope: String,
89 },
90 /// Every scope's health, with what contributes to each
91 Board,
92}