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
//! 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 ;
/// 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 one honest exit (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";
/// 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.