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
//! The conversation's **model row** (§11) and its §9.4 drift clause. "Header"
//! here names the row's shape — what the two dropdowns show, plus the hover and
//! the clause around them — not a seat: the row sits in the bottom settings
//! rows since the settings-seat ruling (§11, bl-2e18).
//!
//! **The row is the selection** (bl-cd2a). The operator's ruling, verbatim:
//! *"Right there, I want essentially that whole line changed to: `<provider> -
//! <model>`. That's it."* So the line carries no `model ·` prefix, no `frozen
//! at <oid>`, and no *change…* button — it carries the pair, in two live
//! dropdowns, and the facts that used to crowd it ride the hover.
//!
//! **What the dropdowns show is what they write: the workspace default.** A
//! conversation is frozen on the config commit its branch forked off, and that
//! is the invariant, not the bug (§9.4) — but a control that displayed the
//! frozen pair would report a write it just made as a no-op, because the tip
//! moves and the freeze cannot. So the pair shown is the config branch tip's,
//! and the freeze is stated beside it exactly when the two have parted.
//!
//! **Drift is derived, never stored.** It is the inequality of two oids the
//! caller already holds: the conversation's governing commit (§5.1 #17) and the
//! workspace's config-lineage tip, which the §7 snapshot already carries. No
//! field records it and no write follows from it — and in particular there is
//! no mid-conversation adoption, which would break the very freeze the clause
//! exists to explain (bl-9786).
use ;
/// The workspace's config-lineage tip (§2.2) as a **seat** holds it (REMOTE
/// §9.4, bl-1eb0): the commit `lernie prompt` forks the next conversation off.
/// Two strings, because two strings are all the picker asks of it — the row
/// labels with the short oid and reads `providers.yaml` at the full one — and
/// because the `CommitNode` this replaced is a git-derivation record a face
/// holding no repository cannot be handed.
/// One config commit as the row reads it: the oid it is, and the
/// `providers.yaml` its tree carries. Two of these — the governing commit and
/// the workspace default's tip — are the whole input to [`conversation_row`].
/// The model row as painted (§11): what the two dropdowns show, the hover that
/// explains the freeze, and — only when the workspace default has moved past
/// this conversation — the clause naming what this one is actually frozen on.
/// What a dropdown shows when the `providers.yaml` it reads declares no such
/// role: the pair is absent, and absence is a value.
const NO_ROW: &str = "(none)";
/// The exit that **discards** (bl-9786), labelled with what it does rather than
/// with where it goes: the affordance only focuses the composer's existing
/// new-conversation verb, so it is a pointer at a gesture, not a second way to
/// start a conversation.
pub const NEW_CONVERSATION_EXIT: &str = "new conversation uses the current config";
/// The exit that **keeps** (bl-2d19), beside it: lernie's own `retarget` verb,
/// which re-forks this conversation off the config commit the workspace runs
/// now and replays every commit it has made onto that base — so its whole
/// history survives the move. Offered under the same condition, because an
/// undrifted conversation is already there.
pub const RETARGET_EXIT: &str = "move this conversation onto the current config";
/// What pressing that says (§11 discoverability rule 3, which is also where its
/// keyboard spelling is named): the verb, its timing, and the one thing an
/// operator must know before spending it — that nothing is thrown away.
pub const RETARGET_HOVER: &str = "Mark this conversation to be re-forked onto the config \
this workspace runs now (lernie retarget). It keeps every message: the \
re-fork lands at the conversation's next step, with the work it has already \
done replayed on top. Type /retarget to do it without the mouse.";
/// Compose the conversation's model row from its governing config and the
/// workspace's current config-lineage tip. The dropdowns show `tip`, because
/// that is what a pick moves; `governing` is what this conversation is frozen
/// on, and it is named only when the two have parted.
/// Compose the §11 birth-config block's model row from the config branch's
/// **head** — the commit a fresh root forks off. No drift clause: a
/// conversation that does not exist yet cannot have parted from anything.
///
/// That head is not a yog choice: lernie 0.0.3's `lernie prompt <repo>
/// <message>` takes no config argument and resolves
/// `ConfigSource::ConfigBranch("config/default")` internally, so "which config
/// is this conversation born on" has exactly one answer and the hover says it.
/// The role a bare row reports and writes — the one that talks to you
/// ([`WORKER_ROLE`]), which the picker's role strip re-scopes while it is open.