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
//! The **live tail** (DESIGN §7.2, bl-54f7): the focused conversation's open
//! `response.json`, followed at frame cadence instead of at the watcher's.
//!
//! Text did not stream back in while the model was thinking or writing: yog
//! reads directly from the stream-in file in the lernie workspace and shows
//! every character as it lands. The mechanism was already there — the fold, the
//! virtual transcript entry, the `Doing` split — but its **cadence** was the
//! whole §7.2 chain: a watcher poll, a `DirtySet` announcement, the 100 ms
//! debounce, a whole-workspace re-derivation. Characters therefore arrived in
//! clumps at the watcher's rhythm.
//!
//! ## What this is allowed to be
//!
//! This is a deliberate, explicit violation of the no-state-in-memory rule:
//! it is purely display, a dead end. So the accumulated text lives in RAM and is **not** re-derivable. What that
//! licence does **not** touch is the frame: it still does no IO. This is a
//! follower on its own thread ([`Follower::spawn`]) publishing into a cell the
//! frame paints from — the derivation worker's own shape applied to one file.
//!
//! Three rules keep the dead end a dead end:
//!
//! - **Only the fold sees it.** The follower publishes into
//! [`TailCell`](crate::state::TailCell); the *only* reader is
//! [`echo::compose`](super::echo::compose), which builds the snapshot the
//! frame paints (`AppModel::snap`). Every gesture, every §8.5 dispatch and
//! every machine-facing reply takes `AppModel::derived` instead — the §7.2
//! partition, *paint reads the fold, gestures read the derivation*. There is
//! no accessor from the model to the tail, so a second consumer cannot be
//! added without deleting that absence first.
//! - **Superseded, never merged.** When the step commits, `NNN-<model>.json`
//! lands and the derivation carries it; the tail is dropped whole at the next
//! subject change or step advance. The two texts are never reconciled
//! character-by-character — the committed entry is the truth and this was a
//! preview of it.
//! - **One subject.** The follower tails the **focused** conversation and
//! nothing else. Tailing every agent in every workspace at frame rate is the
//! version of this that burns the machine.
//!
//! ## Following, not re-reading
//!
//! The follower holds the response file's path, a byte offset and the
//! trailing partial line ([`follow`]). Each pass reads only what was appended, folds the complete
//! lines through the one shared parser
//! ([`fold_stream`](crate::git_tree::fold_stream)) and
//! [`absorb`](crate::git_tree::Stream::absorb)s the result — so the cost is the
//! new bytes, not the response, and a 40 KB answer does not get re-parsed sixty
//! times a second as it grows. Partial-write tolerance is structural twice
//! over: the remainder before the last newline is held back, and the parser
//! skips a line it cannot read.
//!
//! Three things reset the accumulator to empty, and they are one rule — *this
//! is a different stream now*: the focus moved, the agent's latest step
//! advanced, or the file shrank (a truncate or a replaced step dir). Nothing
//! here expires on a clock.
use PathBuf;
use Arc;
use Snapshot;
use crateStream;
pub use ;
/// The focused conversation's live stream, as fresh as the last pass — the one
/// value this whole module exists to publish.
/// Fold the live tail onto the snapshot a frame paints (§7.2). **The only
/// reader of the tail there is**, and it writes into the one field the
/// derivation fills from the same file — so no seat learns a new vocabulary,
/// and the §11 live mark, the flight strip's `N chars streamed`, the roster
/// preview and the transcript's live rows all quicken together off one
/// assignment.
///
/// A tail for an agent this snapshot does not carry writes nothing: the
/// conversation was deleted or has not been enumerated yet, and inventing a row
/// for it is the pending *echo*'s job (§3.4), not this one's.
pub
/// The model's side of the hand-off — the ask, the follower, and the read-only
/// window onto the derivation the memos key on. They live beside the tail
/// rather than in [`AppModel`](crate::AppModel)'s root because that root is
/// deliberately declaration-light (§12) and because this *is* the tail's
/// interface: everything the rest of yog may do with it is these three lines.