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
//! Which failures are still **live** — the ambient error surface's retirement
//! rule (DESIGN §6, §4.2, §11).
//!
//! `ops.jsonl` is append-only and complete: a failure never leaves it unless
//! the operator asks for a fresh trail ([`super::clear`]). But a
//! failure a later clean run of the same verb has superseded is *history*, not
//! a live wound, and painting it with the same prominence sends diagnosis down
//! a false trail — the proven wound: a three-day-old `litany prime` failure,
//! since fixed and re-run green, read as THE error when an unrelated action
//! failed.
//!
//! So prominence is a **projection over the tail at read time, never a stored
//! flag**: walking newest-first, a failed row is live unless a *later* row with
//! the same (`cwd`, verb) did not fail. The verb is the leading two argv tokens
//! — binary plus subcommand (`bl close`, `litany prime`, `yog-step mint`) —
//! because the argv tail carries per-run operands (a ball id, a composed goal)
//! that never repeat, so keying on the whole argv would retire nothing; `cwd`
//! scopes it, so a clean `bl close` in one project leaves a failed one in
//! another alone. Success is the pane's own classifier negated
//! ([`OpRow::failed`]), so no second definition of success can drift from the
//! one the surface paints.
//!
//! A retired failure keeps its row and its ⚠ in the expanded accessory — it
//! loses only ichor and the chip's count. **Absence of a live failure is the
//! record; the log is the history.**
use HashSet;
use OpRow;
/// What a row *is* at read time: the retirement rule's three outcomes, and the
/// one fact both §11 activity seats paint (the chip's failure count, the
/// per-row marker). Derived by [`outcomes`], never stored — and its glyph, hue
/// and **words** have a single home, `theme::op_badge`, so no seat invents its
/// own spelling of "failed" (DESIGN §11, the badge-seat pattern).
/// The §11 activity-accessory summary — the demoted ops pane's collapsed chip:
/// how many ops the tail holds, how many are **live** failures (retired ones
/// excluded, per this module's rule), and how many are **drift** observations
/// (§7.2: a change nobody announced). Pure over the rows the pane would paint,
/// so chip and expansion never diverge — and `drifts` is a *query over the tail*,
/// never a counter yog keeps.
/// Summarize the tail for the collapsed chip (§11): every op counted, the live
/// failures ([`outcomes`]) counted, the drift rows ([`OpRow::drift`]) counted on
/// their own axis.
///
/// **The two alarm axes stop at the operator's ack** (bl-c417,
/// [`super::since_ack`]): `errors` and `drifts` are counted over the rows after
/// the newest ack line only, so a dismissal quiets the chip's ichor exactly as
/// it quiets the §7.3 banners — one watermark, read by both. Drift is quieted
/// with them deliberately: §7.2 files it as an *alarm* ("its catches are
/// alarms, not routine"), and an alarm the operator has said they have seen is
/// what an ack is for.
///
/// `total` is **not** quieted — it counts the whole tail, because it names the
/// rows the expansion renders and an ack removes none of them. A chip that said
/// fewer ops than the pane below it lists would be the one number the operator
/// could catch lying.
/// One [`OpOutcome`] per row, positionally aligned with `rows`: a failed row
/// ([`OpRow::failed`]) is `Failed` unless a *later* row with the same
/// [`verb_key`] retired it (ran clean or handed off — see
/// [`OpOutcome::Detached`]), which makes it `Retired`; a handoff with nothing
/// said against it is `Detached`; everything else is `Clean`. Both of those
/// mark the verb retired going forward (bl-8433: a handoff is the newest fact
/// about the verb, same as a clean run). The whole retirement rule lives here;
/// nothing is stored.
/// The identity a retirement keys on: the row's `cwd` and its **verb** — the
/// leading two tokens of the joined argv, i.e. binary plus subcommand. The
/// operand tail is deliberately dropped (see the module note).