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
//! §11 reconciliation rigor: the ancestry precondition (bl-a1a4), the
//! half-merge guard and the no-resurrection invariant (bl-a04a).
//!
//! The law is that RECONCILIATION IS THE SOURCE OWNER'S JOB. Delivery is a
//! validation and atomic-advance boundary, never a merge queue: for every
//! delivery edge `S -> T` it pins `P = tip(T)` once, requires `P` to ALREADY be
//! an ancestor of `tip(S)`, gates the exact source tree, and advances `T` from
//! `P` by CAS. Fractal by construction — child → `work/<parent>` and root →
//! integration are the same operation at every depth.
//!
//! bl-33db shipped a resurrection THROUGH the delivery gate: its close-fold hit
//! a modify/delete conflict against a sibling's delivered deletion, the
//! conflict got resolved work-side, and the squash silently re-landed the
//! deleted file on main. Three structural answers, all pure plumbing over
//! existing refs (derive-don't-store, zero new state):
//!
//! - **Ancestry precondition** ([`ensure_target_incorporated`]): a source that
//! has not incorporated the pinned target tip is REFUSED before any merge,
//! gate, squash or ref move. Delivery used to merge the target in itself, so
//! a conflict it could not resolve was the only thing that stopped it — and
//! a conflict it COULD resolve landed a tree no human had ever run. Now the
//! only merge is the closer's own, in their own worktree, tested there.
//! - **Half-merge guard** ([`ensure_no_merge_in_progress`]): delivery NEVER
//! concludes a half-merge it finds in the worktree. Capture's
//! `add -A` + commit over a `MERGE_HEAD` would conclude the merge, silently
//! resolving every modify/delete work-side (and committing conflict markers
//! besides) — the resurrection's open door. Resolving is the AGENT's job;
//! their resolution merge commit is ordinary work on `work/<id>`.
//! - **No-resurrection invariant** ([`ensure_no_resurrection`]): at squash,
//! the squash's changed paths (diff vs the pinned fold base, bl-8b89) must be a subset
//! of the paths authored on `work/<id>` since its fork — every non-merge
//! commit's changed paths plus each reconciling merge commit's resolution
//! paths (its combined `--cc` diff; a conflict resolution IS a work commit,
//! so it counts). An excess path means the squash carries something the task
//! never wrote — a resurrection or a leak — and aborts the close NAMING it.
use BTreeSet;
use io;
use Path;
use crateProject;
/// The ANCESTRY PRECONDITION (bl-a1a4): the pinned target tip `base` must
/// ALREADY be an ancestor of `branch` before delivery does anything. It is the
/// whole of "the source owner reconciles" — one `merge-base --is-ancestor`
/// over two refs the delivery already holds, no state, no lease, no queue.
///
/// Delivery used to merge `base` into the work branch itself, which made close
/// a merge queue: a clean target advance was folded in automatically and the
/// gate then ran on a tree that had never existed anywhere a human could test
/// it, while a conflicting one was the only advance that stopped the close.
/// Both are the same mistake — the source owner is the only party that can
/// decide what incorporating the target MEANS. So a stale source refuses, and
/// the refusal names S, T and P and prescribes the remedy: merge or rebase the
/// target into the source worktree, resolve and test THERE, then retry.
///
/// `target` is the branch NAME the operator thinks in; `base` is the pinned SHA
/// the delivery actually acts on (bl-9522/bl-8b89 — one read serves as the
/// precondition's comparison point, the squash parent, the no-resurrection base
/// and the CAS old-value), so the voice carries both.
pub
/// The half-merge guard: refuse to act in a worktree whose merge is still in
/// progress (`MERGE_HEAD` exists). Runs BEFORE capture — `add -A` + commit
/// there would conclude the half-merge with a silent work-side resolution of
/// every modify/delete (the bl-33db resurrection). The agent resolves and
/// commits the merge themselves, then retries the close.
pub
/// The no-resurrection invariant, checked at squash time: every path the
/// squash would change (vs `base`, the PINNED integration tip the gated source
/// tree already contains — bl-8b89) must have been authored on the work branch
/// since its fork. Authored = the union of `--name-only` paths over the
/// commits on `branch` not on `base` — `--cc` so the closer's own reconciling
/// merge commit contributes exactly its resolution paths (where the result
/// differs from every parent), nothing the target brought in. An excess path
/// aborts the close naming it. Comparing against the LIVE tip made a mid-gate sibling
/// landing read as excess (a false resurrection abort naming innocent paths);
/// against the pin, a mid-gate move is the delivery CAS's clean rejection and
/// excess means only a real resurrection or leak.
///
/// `--no-renames` on BOTH reads (bl-4235, same reason as bl-ae74): the question
/// is WHICH PATHS A COMMIT TOUCHED, and rename pairing is a presentation choice
/// with no business changing that answer. Detection is not unconditional — it is
/// bounded by `diff.renameLimit`, so a big tree-to-tree diff can blow the budget
/// and silently stop pairing while a small per-commit diff stays under it. Then
/// a rename `a`→`b` reports as `{a, b}` on one side and `{b}` on the other, `a`
/// lands in `excess`, and an innocent close aborts naming a path the work branch
/// demonstrably authored. Unpaired, every rename is its delete plus its add on
/// both sides and the subset relation holds by construction.
pub
/// One `--name-only` listing → a path set (blank lines dropped).