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
269
270
271
//! The compaction merge (ARCH §2.6, §2.7, §5.5) — the one merge left in
//! the system now that merge-back is gone (§2.6).
//!
//! A compactor forks off a **checkpoint commit** `C` (the dispatching
//! branch's tip at dispatch) and rewrites only what existed at `C`:
//! deleting superseded transcript entries, landing a new summary, and
//! nominating superseded work products for deletion. The live agent keeps
//! stepping past `C`, and its commits since `C` only *append* new
//! sequence filenames (transcript immutability, §2.3) — so the two write
//! sets are disjoint and the merge is conflict-free by construction. The
//! agent's own executor lands it `--no-ff` at a step boundary; the merge
//! commit *is* the context rebuild point (§5.5).
//!
//! **The one theoretical overlap — live-branch-wins.** A compactor may
//! nominate a *work product* the live agent has rewritten since `C`. That
//! is the sole conflict class: transcript entries never collide (the live
//! branch only appends new filenames) and a fresh `summary/<NNN>.md`
//! never collides (its seq is past every prior summary, §2.7). The
//! overlap surfaces as a git modify/delete conflict, and the executor
//! resolves it **live-branch-wins**: it stages the working-tree state
//! (git leaves the live agent's version in the worktree on a
//! modify/delete), which drops the compactor's deletion. A dropped
//! deletion is lost compaction, never lost work — the same worst case the
//! deletion-only toolset already guarantees (§2.7).
//!
//! `git add -A` after a `--no-ff --no-commit` merge realizes this in one
//! move: a clean merge is already fully staged (the add is a no-op), and
//! a modify/delete conflict leaves the live version in the worktree,
//! which the add stages — resolving the conflict by keeping ours.
//!
//! **Anything else is refused, loudly.** `git add -A` is a *resolution*,
//! and it is only a correct one for the single overlap class above. When
//! git cannot merge a path on its own it writes `<<<<<<<` / `=======` /
//! `>>>>>>>` into the working tree, and an unqualified `add -A` would
//! stage that markup and commit it — into `summary/**`, which §5.2
//! composes into every subsequent model call on this branch. That is not
//! lost compaction, it is *corrupted context*, the one outcome §2.7
//! promises can never happen. So the merge asks git which paths it had to
//! mark ([`content_conflicts`], read from the index stages, not guessed)
//! and, for any of them, aborts the merge and marks
//! `refs/lernie/conflicted/<compactor-id>` instead — the §2.6 decline,
//! the same escape hatch the work-product transfer uses. Nothing lands;
//! the branch continues uncompacted. This is the third defect of
//! bl-a9eb (yog bl-ebbd), where three unresolved conflicts were committed
//! into a live `summary/001.md` at a root branch's tip.
//!
//! **Filtered to the compaction product** (§2.6, §2.7). A compactor is an
//! ordinary child, so its branch also grew its *own* context since `C`:
//! the `goal.md` and `soul.md` its dispatch commit rewrote, and the
//! transcript entries under `messages/**` its step loop appended. None of
//! that is the dispatching branch's context — it is the compactor's
//! private dialog, whose record is its own ref. What the merge is
//! specified to land is exactly what the two-tool toolset produces
//! (§2.7): the new `summary/<NNN>.md` and the nominated deletions. So the
//! staged merge is filtered before it commits — every path the merge
//! would *add or rewrite* outside `summary/` is restored to the
//! dispatching branch's own version, while deletions (the whole point of
//! compaction) pass untouched. This is the §2.6 work-product transfer's
//! principle in mirror image: that channel admits work products and
//! excludes branch-scoped context; this one admits the branch's own
//! context product and excludes the compactor's private dialog.
use Error;
use crateGitRunner;
use crateworkspace;
use ;
use Path;
/// The compaction product's sole *addition* surface (ARCH §2.7): the
/// summary `write_summary` writes. A git exclude pathspec — the same
/// filtering mechanism the work-product transfer uses (§2.6).
const SUMMARY_EXCLUDE: &str = ":(exclude)summary";
/// Outcome of a compaction merge attempt against the dispatching branch.
/// Land the compactor branch `compactor_id` into the dispatching branch
/// checked out at `parent_worktree` (ARCH §2.6). The checkout's `HEAD`
/// *is* the dispatching branch (§2.3), so the merge base derives from
/// ancestry and no branch name is passed. `--no-ff --no-commit` sets the
/// merge up, `git add -A` resolves any work-product modify/delete overlap
/// live-branch-wins (module docs), and the commit lands the two-parent
/// merge — the §5.5 rebuild point.
///
/// A compactor whose ref is already an ancestor of `HEAD` (nothing to
/// land) leaves no `MERGE_HEAD`; that is [`MergeOutcome::NoOp`], not an
/// error. A merge that fails to even begin (a bad ref) surfaces loudly.
/// A merge git had to write conflict markers into is
/// [`MergeOutcome::Conflicted`]: aborted, marked, nothing committed
/// ([`decline`]).
/// Drop the compactor's private dialog from the staged merge (module
/// docs, §2.6/§2.7). The comparison is the staged merge result against
/// `HEAD` — still the dispatching branch's own tip under `--no-commit` —
/// so its three classes name themselves: a path **added** outside
/// `summary/` is a compactor transcript entry, a path **rewritten**
/// outside `summary/` is its `goal.md`/`soul.md` (or a filename collision
/// git resolved into a conflict), and a path **deleted** is the
/// compaction the merge exists to land. Both non-deletion classes are
/// restored to the dispatching branch's version — an addition by removal,
/// a rewrite by checkout — leaving only the summary and the deletions.
/// Paths the staged merge would land outside `summary/` under diff class
/// `filter` (`A` added, `M` rewritten), relative to the dispatching
/// branch's tip. `--no-renames` keeps the classes exhaustive: an
/// add/delete pair must not collapse into an `R` that escapes both.
/// Run `args` extended with `paths`, or nothing at all when `paths` is
/// empty — the general path with empty inputs (`docs/PRINCIPLES.md`), not
/// a special case: git would read a pathspec-less `rm` as an error.
/// True iff a merge is in progress in `parent_worktree` — i.e. `MERGE_HEAD`
/// resolves. Derived from git state, never a stored flag: `git rev-parse
/// --verify -q MERGE_HEAD` prints the sha and exits zero when a merge is
/// underway, and exits non-zero (captured as `Err`) when none is.