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
//! Compactor toolset (ARCH §2.7) — `write_summary` and
//! `mark_for_deletion`, and nothing else.
//!
//! These are the **two** tools a compactor agent may call, and they are
//! **built into the primitive, not declared in `providers.yaml`** (§2.7):
//! the compactor's toolset is this fixed pair, injected by the harness for
//! the compactor role alone, never assembled from a role's `tools:` list.
//! The narrowness is the point — giving the compactor no general
//! filesystem write surface makes "deletion-only" a **structural**
//! property rather than a disciplinary one: the worst failure mode is lost
//! information, never corrupted information (§2.7, §2.6 live-branch-wins).
//!
//! - [`write_summary`] writes `summary/<NNN>.md` on the compactor branch —
//! the one location it may create, picked by scanning the directory.
//! - [`mark_for_deletion`] nominates a file for removal; the harness
//! applies the deletion at commit time. "Applied at commit time" is
//! realized by staging the removal (`git rm`) so the compactor step's
//! own commit carries it (§2.3), and the compaction landing (§2.6)
//! then applies it to the base — subject to live-branch-wins on any
//! work-product overlap in the replay.
//!
//! The deletions are **deletion-only structural**: `git rm` can remove but
//! never write content, so a compactor cannot corrupt a work product even
//! by defect. The compactor decides relevance against the dispatching
//! branch's goal (`goal.md`), which its inherited worktree carries (§2.7).
//!
//! **The goal is not compaction-eligible** (§2.7, bl-898f). A goal has two
//! projections written at one dispatch and neither ever rewritten — the
//! pinned `goal.md` (§2.8) and the **dispatch entry**, the transcript entry
//! the same text was deposited as through the front door (§2.11: a root's
//! opening user message, a child's dispatch message). The compactor is
//! shown one of them — its own goal quotes the dispatching branch's
//! `goal.md` verbatim (§2.7) — while the other sits in the transcript it
//! is told to prune, so the dispatch entry is the one entry that reads as
//! *pure duplication* to a model nominating superseded files. It was
//! nominated and deleted in practice, and what it deleted was the
//! operator's only copy of the prompt the conversation exists to serve.
//! [`mark_for_deletion`] therefore declines that path — at the nomination,
//! in-band, so the compactor's summary is never premised on a deletion
//! that did not happen. Declined at the door rather than dropped at the
//! landing because the fact is knowable from the path alone;
//! live-branch-wins is dropped at the landing precisely because *its* fact
//! (a race with the live branch) is not (§2.6).
use Error;
use crateMESSAGES_DIR;
use crateGitRunner;
use Path;
/// Built-in tool name: write the next `summary/<NNN>.md` (ARCH §2.7).
pub const WRITE_SUMMARY: &str = "write_summary";
/// Built-in tool name: nominate a branch-relative path for removal
/// (deletion-only structural, ARCH §2.7).
pub const MARK_FOR_DELETION: &str = "mark_for_deletion";
/// Branch-relative directory holding compaction summaries (ARCH §2.7).
/// Lives at the worktree root so the manifest's role-keyed `pinned:
/// [summary/**]` rule (§5.2) sees it.
pub const SUMMARY_DIR: &str = "summary";
/// Width of the zero-padded summary-seq in summary filenames
/// (`001.md`, `002.md`). Matches the step-seq width (§2.3) so the two
/// on-disk layouts read uniformly.
const SUMMARY_SEQ_WIDTH: usize = 3;
/// Transcript counter of the **dispatch entry** — the entry every
/// branch's opening prompt lands as (module docs, §2.3, §2.11). The
/// counter is monotonic and never reused (`dispatch::transcript`), so
/// `001` names that entry for the life of the branch.
const DISPATCH_SEQ: u32 = 1;
/// Write `summary/<NNN>.md` on `worktree`, picking the next-available
/// seq by scanning the directory. Returns the branch-relative path of the
/// written file for the subsequent `git add`.
///
/// Seq is branch-global over the summary directory's contents: a branch
/// may compact several times (§2.7), and reading existing seqs here means
/// every checkpoint shares one numbering rule.
pub
/// Nominate the branch-relative `path` for removal (ARCH §2.7). Realized
/// as `git rm -r -- <path>` inside the compactor `worktree`, staging the
/// deletion so the compactor step's commit carries it (§2.3) — the
/// "applied at commit time" contract. **Deletion-only structural**: this
/// can only remove, never write, so a compactor cannot corrupt content.
///
/// Two nominations are **declined loudly** rather than silently ignored
/// (`docs/PRINCIPLES.md` "Decline illegal operations"), and the decline
/// reaches the model in-band as an `is_error` `tool_result` (§3.3):
///
/// - the branch's **dispatch entry**, which is the goal in transcript form
/// and is not compaction-eligible (module docs, §2.7);
/// - a path that does not exist on the branch — a compactor nominating a
/// nonexistent file is a defect worth surfacing, and `git rm` errors on it.
pub
/// Whether the branch-relative `path` names the branch's dispatch entry
/// (module docs). Derived from the name alone — the same `NNN-` prefix
/// reading `dispatch::transcript`'s counter uses — so it needs no tree,
/// no config and no state, and it holds for a `.md` delivery and a
/// resumed conversation's inherited first entry alike.
/// Pick the next summary-seq: one more than the highest existing
/// `<NNN>.md` file in the directory. Non-`.md` files and files whose
/// stems don't parse as integers are skipped so an operator-dropped
/// note never fouls numbering.