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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
//! The workspace physical model (ARCH §2.2–§2.3).
//!
//! A **workspace** is one git repository at `<workspace>/repo.git`
//! (bare), holding config branches (`config/<name>`) and agent refs
//! (`agents/<agent-id>`). There is no `main`: no branch is a trunk, and
//! which advancement rule a ref lives under is derived from its path
//! prefix, never recorded anywhere else (§2.3). Agent worktrees are
//! materialized as siblings under `<workspace>/agents/`; `steps/` and
//! `inbox/` sit at the workspace root, outside every worktree (§2.2).
//!
//! Control files — `workflow.yaml`, `manifest.yaml`, `providers.yaml`,
//! `souls/`, `version` — are read from the agent's **governing config
//! commit**: the nearest ancestor of the agent's branch reachable from
//! any `config/*` ref, derived from ancestry (`git merge-base`) and
//! never stored (§2.2, PRINCIPLES "Single source of truth").
//!
//! This module owns the path/ref arithmetic and the ancestry
//! derivation; it holds no state and performs no writes beyond what the
//! injected [`GitRunner`] is asked to run — the one exception is
//! [`agent_name::settle`], which keeps the read and write halves of the
//! agent-name fact in a single home. The two guards every verb runs
//! before any of it — *is this a workspace*, *does this agent exist* —
//! are [`guard`], re-exported here.
use crateGitRunner;
use io;
use ;
/// The workspace repository, bare, at `<workspace>/repo.git` (§2.2).
pub const REPO_DIR: &str = "repo.git";
/// Directory under the workspace root where agent worktrees live as
/// siblings — `<workspace>/agents/<agent-id>/` (§2.2).
pub const AGENTS_DIR: &str = "agents";
/// Ref-namespace prefix for agent branches: `agents/<agent-id>` (§2.3).
pub const AGENT_REF_PREFIX: &str = "agents/";
/// Ref-namespace prefix for config branches: `config/<name>` (§2.3).
pub const CONFIG_REF_PREFIX: &str = "config/";
/// The config lineage a fresh root agent forks off when the start names
/// none (§2.3 *Fresh start* — the head of a config branch; `lernie new`
/// authors this one, and `lernie config` advances it by default). The
/// bare name is the vocabulary both command lines use; [`config_ref`]
/// applies the prefix at the git boundary.
pub const DEFAULT_CONFIG_NAME: &str = "default";
/// The root every per-agent **mark** ref lives under:
/// `refs/lernie/<kind>/<agent-id>`. The kinds spell their own prefixes
/// where they are written (§2.6 `conflicted`, §6 `budget-exhausted`,
/// `abandoned`, `notify`, §3.3 [`cwd`], §8 `returned`); this is the namespace they share, so a
/// consumer that must reach *every* mark of an agent — the retention
/// delete (§9.2) — enumerates the root instead of keeping a list of
/// kinds that would go stale the day a fifth one lands.
pub const MARK_REF_ROOT: &str = "refs/lernie/";
/// Ref-namespace prefix for the §2.6 **decline** mark,
/// `refs/lernie/conflicted/<agent-id>`. One namespace, one home here in
/// the ref-naming module, written by every operation that must refuse
/// rather than guess: the declined work-product transfer
/// (`prompt::dispatch::transfer`) and the declined compaction landing
/// (`prompt::compactor::land`). The UI renders it as `declined-transfer`
/// alongside the orthogonal budget-exhausted mark (§3.5, §7.1).
pub const CONFLICTED_REF_PREFIX: &str = "refs/lernie/conflicted/";
/// A config branch ref, `config/<name>` (§2.3). The prefix is the kind
/// (config vs agent), applied only at the git boundary — the bare name
/// is what a user names on the `lernie config` command line.
/// The harness-facing control paths the dispatch commit removes from an
/// agent's tree when it forks off a config commit (§2.2 "Control is
/// read from the config commit; worktrees hold only context").
///
/// `descriptions/**` is not among them — it *is* context (§3.3) — but it
/// is not inherited whole either: the same commit prunes it to the
/// forking role's `tools:` grant, which is a query on the tree rather
/// than a fixed path list and so lives with the prune
/// (`prompt::dispatch::step_commit::descriptors`), not here.
pub const CONTROL_PATHS: & = &;
/// `<workspace>/repo.git` — where every ref-level git command runs.
/// `<workspace>/agents/<agent-id>` — the agent's worktree (§2.2).
/// The agent's branch ref, `agents/<agent-id>` (§2.3). The id — the
/// full hyphenated descent — is the primary identifier everywhere
/// (inbox and steps namespaces, worktree dir, `LERNIE_CONV_BRANCH`);
/// the prefix is applied only at the git boundary.
/// Enumerate the short names under one ref-namespace prefix, prefix
/// stripped (§2.3 — the prefix is the kind, derived from the path, never
/// recorded). The workspace keeps exactly two registries, and both are
/// this one query: the ref namespace *is* the registry.
/// Enumerate the workspace's agent ids: every `agents/*` ref, prefix
/// stripped (§2.3). This is the §8 enumeration seam: scan/stop/budget
/// candidate sets read agent branches from here, never "every branch
/// except main".
/// Enumerate the workspace's config lineage names: every `config/*` ref,
/// prefix stripped (§2.3). The bare names are what a user names on the
/// `lernie config` command line, so this is both the existence query for
/// a `--from <source>` and the pool a decline names.
/// The **governing lineage** of the revision `rev`: every `config/*`
/// ref whose history reaches it, paired with the ancestor it
/// contributes (`git merge-base <rev> <head>`). A config lineage sharing
/// no ancestor with `rev` — a fresh orphan config — reaches it through
/// nothing and is absent from the result.
///
/// `rev` is a revision, not an id: an agent's ref ([`agent_ref`]), a
/// config branch, or any commit of either — the same set §2.3 admits as
/// fork points, so one derivation answers for a branch that exists and
/// for a fork point a branch is about to be cut from.
///
/// This is the candidate set [`governing_config`] folds to one commit,
/// and the ref set `archive::bundle` carries (§9.2): a config branch
/// that advanced past the fork is *not* an ancestor of the agent, yet
/// it is the ref the merge-base is taken against, so the lineage
/// travels **at its heads** and a replayed workspace re-derives over
/// the same candidate set rather than an approximation of it.
/// Derive the **governing config commit** of the revision `rev`: the
/// nearest ancestor reachable from any `config/*` ref (§2.2). Each ref
/// of the governing lineage ([`config_lineage`]) contributes the shared
/// ancestor on its lineage; the governing commit is the *descendant*
/// among the candidates (nearest to `rev`). Derived from ancestry,
/// never stored. Loud when no config lineage reaches `rev`, and loud
/// when two candidates are incomparable — both mean a defective
/// workspace, declined rather than guessed (PRINCIPLES "Decline illegal
/// operations").
///
/// One derivation serves both readings of §2.2, because they are one
/// question: an existing agent asks it of its own ref, and a fresh root
/// asks it of the ref it is about to fork off (§2.3 *Any ref is a legal
/// fork point*). A config branch's head answers itself — it is its own
/// nearest config ancestor — so "fork off a config head" needs no
/// second rule, and **fork is the freeze** holds whatever the fork
/// point is: the grants derive from the governing config commit, never
/// from the fork point's own tree (§3.3, §5.1).
/// Of two candidate ancestors of one branch tip, keep the descendant —
/// the nearer one. Incomparable candidates are declined loudly.
/// Read one control file's contents from a config commit's tree
/// (`git show <commit>:<path>`, §2.2 "Control is read from the config
/// commit"). The worktree is never consulted.
/// Does `path` exist in the config commit's tree? (`git cat-file -e`.)
pub use ;
pub