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
//! The operator's own two lines in the trail (DESIGN §4.2 as amended, §7.3,
//! §11): the **ack** that quiets every failure-derived alarm, and the **clear**
//! that starts a fresh trail.
//!
//! The complaint both answer is one complaint: a failure banner clears only
//! when a *newer successful op of the same origin* lands (§7.3), so an operator
//! who reads the error and decides not to retry stares at it forever; and the
//! §11 chip's ⚠ count derives from an append-only file, so it can never reach
//! zero either. Neither had a dismissal, and the log had no verb that ends a
//! trail.
//!
//! **Dismissing is an action, so it is an ops line.** It is not a stored flag,
//! a second state home, or a per-surface bit: `ops.jsonl` stays the single
//! source of truth, and the ack is a **global seen-watermark** derived from it
//! — every failure-derived alarm considers only the rows *after* the newest ack
//! ([`since_ack`]). One line shape, no per-origin variants: dismissing from
//! anywhere means "I have seen what is on screen now". A **new** failure
//! afterwards lands after the watermark and re-alarms, which is the whole point
//! of watermarking rather than deleting.
//!
//! **Ack quiets alarms; it never hides history.** The expanded trail renders
//! every row it always did — the acked failures and the ack line itself — and
//! the chip's `N ops` still counts the whole tail, because that number names
//! the rows the expansion shows and an ack removes none of them.
//!
//! Both lines are ordinary completed [`YOG_STEP`] rows
//! ([`OpEntry::step_done`]), exit `0`, [`Origin::World`] — yog's own doing, on
//! no operator surface, so neither can banner anywhere (§7.3) and neither reads
//! as a failure. They obey every rule the other line shapes obey: built through
//! [`build_line`], so the ≤[`CAP`](super::CAP)/PIPE_BUF atomicity bound holds
//! for them too.
use fs;
use ;
use Path;
use OpRow;
use ;
/// `argv[1]` of the **ack** line: the operator has seen every alarm on screen.
/// The whole vocabulary of the watermark — [`since_ack`] recognises a row by
/// this and nothing else.
pub const ACK_STEP: &str = "ack-failures";
/// `argv[1]` of the **clear** line: the first row of the trail it begins.
pub const CLEAR_STEP: &str = "clear-trail";
/// Append the **ack** line (§4.2): the operator's seen-watermark over the trail.
/// `ts` is the caller's clock stamp, as for every other line — this module
/// reads no clock.
/// **Clear the trail** (§4.2 as amended, §11): truncate `ops.jsonl` and log the
/// clear itself as the new trail's first row, so the discard is an action with
/// a record like every other action.
///
/// Written through an `O_APPEND` handle that is truncated with `set_len`, never
/// a positioned write: the clear line therefore lands at whatever the end of
/// the file is *at write time*, so a concurrent instance's append between the
/// truncate and the write is preserved rather than overwritten. The reader is
/// stateless (it re-reads the whole file, §4.2), so a shrinking file needs no
/// handling of its own.
/// The shared shape of both lines: a completed `["yog-step", <step>]` row in
/// the state root, attributed to [`Origin::World`] and to the seat that asked
/// (bl-e59e) — these two are the operator's own gestures about the trail, so
/// *whose* ack quieted a shared workspace's alarms is exactly the fact a second
/// seat needs.
/// The rows an alarm may consider: everything **after** the newest ack line, or
/// all of them when the operator has acknowledged nothing. The one derivation
/// of the watermark — the §7.3 standing a row is answered with
/// ([`super::standings`]) and the §11 chip's counts ([`super::activity`]) both
/// read it, so a banner and a chip can never disagree about what has been
/// seen.
///
/// Slicing off a *prefix* cannot change what the rows that remain mean: §6's
/// retirement looks only at rows *later* than the one it judges
/// ([`super::outcomes`]), so an outcome computed over the suffix is the outcome
/// it had over the whole tail. `pub(crate)` — a borrowed slice is an internal
/// accessor, never a boundary type.
pub
/// Whether `row` is an ack line — its leading two argv tokens, the same
/// two-token verb reading §6's retirement key uses.