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
//! **The agent's balls space** (DESIGN §16.3, the per-agent ruling):
//! where one agent's task tracking lives, and the one var that carries it.
//!
//! The ruling: by default each agent gets its own balls branch for tracking;
//! an agent's branch can be set at launch; subagents are passed their parent's
//! space by default; and an agent can amend its own branch to change its
//! config. A **space** is the whole of that: balls' state home (the
//! clone bundle — landing, store checkout, worktrees) and balls' config home
//! (the §4 layer-2 `config.toml` that names the store branch), together.
//!
//! **One var carries it: `YOG_MARKS`**, layered onto an agent's spawn exactly
//! as [`YOG_WALL`](super::wall::YOG_WALL) is, so the whole descendant tree
//! inherits it — that IS the subagent clause, with no mechanism of its own.
//!
//! - **Absent = the world's space** ([`Space::world`]): balls' state stays
//! `<world>/state` (where every clone yog's board reads already lives) and
//! its config home becomes `<world>/config`. yog's own `bl` verbs, the §5.1
//! #2 board reads, and every agent *pointed at a project* run here, so the
//! project's board is one store and stays instantly consistent.
//! - **Present = the agent's own space** ([`Space::own`], `<wall>/marks`): a
//! private clone bundle AND a private balls config home, so that agent's task
//! churn shares nothing with the project's board — the default the ruling
//! asks for.
//!
//! **`<world>/config` is not cosmetic — it closes a real leak.** balls reads
//! `$XDG_CONFIG_HOME/balls/config.toml` (a layer that OUTRANKS the landing, so
//! it decides `tasks_branch`) and `$XDG_CONFIG_HOME/balls/default-config/` (the
//! template `bl prime` founds a landing from). §16.2 nested balls' state and
//! left `XDG_CONFIG_HOME` ambient, so both resolved to the operator's own
//! `~/.config/balls` — and a stale `default-config` there (one naming the
//! retired `tracker` plugin) made every landing yog founded prune its whole
//! plugin schedule: no `bl-tracker` at any phase, so yog's stores never fetched
//! and never pushed. Nesting the config home is what puts yog outside that
//! blast radius (bl-e47b's investigation).
//!
//! **The branch is written into the space's own `config.toml`, in balls'
//! schema, and read back from it.** It cannot be `bl conf set task-branch`:
//! that write is scope-keyed to the LANDING, and a landing is per *clone* —
//! i.e. per invocation path — so it binds a project, not an agent, and an agent
//! that runs `bl` in three directories would need three writes. balls' layer-2
//! config is the one place a value covers every clone in a space, which is what
//! "the agent's branch" means. It stays balls' own file, balls' own key and
//! balls' own precedence — yog stores no setting of its own shape, and `bl
//! conf` remains the authority on what resolved (it reports the winning layer
//! by name, `xdg`). Severability holds: deleting the space deletes the policy.
use io;
use ;
use crate;
use crateEnv;
/// The one var naming an agent's own balls space (§16.3). Absent = the world's
/// space, which is the project's board — the space every agent pointed at a
/// project uses, and the one yog's own verbs and reads run in.
pub const YOG_MARKS: &str = "YOG_MARKS";
/// balls' default store branch — the project's stable contract (`balls::
/// DEFAULT_TASKS_BRANCH`), and what a space with nothing written reads as.
pub const SHARED_BRANCH: &str = "balls/tasks";
/// balls' §4 layer-2 config file under a config home: `<home>/balls/config.toml`
/// (`balls::layout::Xdg::user_config`, reproduced as path algebra so the fold is
/// pure). The single home of a space's store branch.
/// balls' two home directories for one space (§16.3) — state (the clone bundle)
/// and config (the layer-2 `config.toml` + the seed template). Owned and
/// concrete; every consumer hands them to `balls::layout::Xdg`.
/// The `tasks_branch` value in a balls layer-2 config body, if it names one.
/// The body is balls' TOML, but yog authors it and [`lawful`] confines a branch
/// to characters no TOML escape can reach — so the read is the same one line
/// back, not a parser yog would have to take a dependency for.
/// Is `branch` a lawful store branch to write (§16.3)? A git ref name with no
/// whitespace and no TOML-escaping character, and never balls' own landing
/// branch — which balls refuses outright ("one branch cannot back two
/// checkouts"), so refusing it here states the same invariant one step earlier,
/// at the field, in §3.1's idiom.
/// balls' landing branch (`balls::LANDING_BRANCH`) — the one name a store
/// branch may never take.
const LANDING_BRANCH: &str = "balls/config";
/// The double quote, spelled by codepoint: a bare char literal of it opens a
/// string as far as the §12 citation sweep's scanner is concerned, and one
/// escape hatch is cheaper than teaching that scanner char literals.
const QUOTE: char = '\u{22}';
/// An agent's own space root under its workspace wall: `<wall>/marks`. The wall
/// is already the sphere's one private layer (§16.2), and the §3.1 name that
/// keys it is the same name that is the ball claimant (§3.2) — so the claimant
/// and the space it claims into are one fact, never two that can disagree.
/// The space standing in `env` (§16.3): `YOG_MARKS` when an agent's own space
/// is layered on, else the world's. The one resolution, used by every read and
/// by the embedded `bl` arm alike, so the space yog reads and the space an
/// agent's `bl` writes are one answer.
/// The spawn layer for an agent launched onto its **own** space (§16.3): the
/// `(var, value)` pairs layered on top of the world's overrides and the wall's,
/// through the same [`Cli::and_env`](crate::cli_outbound::Cli::and_env) seam.
/// Empty for a launch pointed at a project — that agent's `bl` is the board's
/// own, and an absent var is exactly that.
/// Read a workspace's tracking branch (§16.3) — the state `/marks` reports and
/// the pane renders. Never spawns: the value's one home is the space's own
/// config file, and an unfounded project (or no project at all) is no obstacle,
/// which is what makes the launched-then-pointed-at-a-project case answerable.
/// Point a workspace's own space at `branch` (§16.3): write `tasks_branch` into
/// balls' layer-2 config for that space, log the write to `ops.jsonl` (§4.2, the
/// mutation-logging discipline), and hand back the branch **re-read** — what
/// landed, never an echo of what was asked.
/// The refusal an unlawful branch earns, said once — the grammar states it
/// before dispatch and [`apply`] states it again at the write, so a typed line
/// and a forced call cannot word the same fact differently.
pub const REFUSAL: &str =
"name a store branch: one word, no quotes, and not balls' own landing branch (balls/config)";
/// Write `tasks_branch = "<branch>"` as the space's whole layer-2 config. The
/// file is yog's to author in full: a space is one agent's, and its only balls
/// config is the branch — so a merge-preserving edit would be machinery for a
/// key nothing else ever writes.
/// The config body, said once — the write emits it and [`parse_branch`] reads
/// it back. One key, quoted; [`lawful`] has already refused anything a quote
/// would have to escape.
/// Append the write's outcome to `ops.jsonl` (§4.2). A file write is not a
/// spawn, so it rides the §4.2 non-spawn step shape the start flow's own
/// `yog-step` rows use — the path is the subject, the exit says whether it
/// landed.