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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
use *;
/// **THE RATCHET (#1981).** The slash surface may only shrink.
///
/// Two numbers, because they answer different questions: `COMMANDS` is
/// what the consolidation must reduce, `all_tokens` is what an operator
/// can actually type — aliases included, since a removed alias is a
/// removed affordance.
///
/// Walked from the dispatch, not from `help_lines()`: the help had
/// already drifted by eleven undocumented commands when this was armed.
///
/// # 44/54 → 42/52: `/roadmap` absorbs `/tree` and `/plan` (#2009 PR12)
///
/// The dispatch had already folded them — `/tree` ran `/roadmap show` and
/// `/plan` ran `/roadmap` — so what this slice does is make the REGISTER
/// say what the code had been doing, and give the fold the retirement rule
/// the others got.
///
/// # 26 → 25: `/plan` never wrote either
///
/// Its row was `Absorb`/`Missing`, as though it set something. It was an
/// alias that FORWARDED to `/roadmap` — `chat.rs` rewrote the line and
/// handed it on. A row that cannot write never owed a receipt, so this is
/// the same truthing shape as `/memory` and `/loadout` (PR3) and
/// `/retrieval` (PR11): the debt was never its own.
///
/// Four of the six reductions so far are this — a row that was counted
/// because nobody had read it, not because it stopped writing. That is
/// worth watching: the remaining 25 should be checked for the same before
/// anyone assumes they all need an event journal to clear.
///
/// # 57/72 → 44/54: the navigator folds into `/nav` (#2009 PR11)
///
/// **Thirteen commands and five aliases, in one slice** — the largest
/// reduction of the cut, and the cheapest, because they were already one
/// parser with one verb match. `parse_nav_command` strips the `nav` and
/// matches the verb it always matched, so the retired names and their
/// replacements are the same line by the time anything decides what to do.
///
/// Every verb keeps its OWN help line, spelled `/nav <verb>` — the doc is
/// explicit about that, and it is why the fold is a subcommand rather than
/// a single opaque entry point. Thirteen discoverable rows became thirteen
/// discoverable rows under one name; what left is thirteen top-level
/// claims on the operator's memory.
///
/// # 58/73 → 57/72: `/conversation` folds into `/resume` (#2009 PR6b)
///
/// One conversation surface: list, show, restore, rename and delete are
/// `/resume` subcommands now, sharing the retired verb's parser and
/// handler so the two doors cannot drift.
///
/// **The row keeps `Receipt::Missing`.** Retiring the verb does not pay
/// its debt — a delete still records nothing durable, and §4.4 parks the
/// conversation operations for the event journal rather than minting
/// `SettingValue` variants for them. A retirement that quietly cleared the
/// count would be the most tempting wrong answer available here.
///
/// # 59/74 → 58/73: `/recall` folds into `/resume find` (#2009 PR6)
///
/// `/resume <token>` already ran the same FTS5 search; what `/recall` had
/// that it lacked was *searching without reopening*, since a token that
/// resolves as an id reopens that conversation. That is now `find`, a
/// subcommand of the verb the operator already reaches for — so the
/// capability survives while the top-level name does not.
///
/// # 68/83 → 59/74: the `/status` fold (#2009 PR3)
///
/// Nine reads stop being top-level verbs and become topics of one:
/// `/info` `/config` `/version` `/workspace` `/byline` `/memory`
/// `/loadout` `/models` retire into `/status <topic>`, and `/docs` into
/// `/help docs`. **The first real reduction of the cut**, and it pays back
/// PR2's raise with one to spare.
///
/// The verbs still work — see `Surface::Retired`: a retired READ may
/// still read, because §3.3 requires reads to keep working on a pipe.
/// What retires is the claim on the surface, which is what these two
/// numbers measure.
///
/// # 63/76 → 68/83: registering the ghosts (#2009 PR2)
///
/// Five commands an operator could type today, advertised in
/// `help_lines()`, reaching real handlers, and counted by neither of these
/// numbers: `/new` (`/clear`), `/end`, `/restart`, `/start`, `/cd` — plus
/// `inspect`, a proven alias of `/type` (`navigator_cmds.rs:105` matches
/// both in one arm).
///
/// **A shrink ratchet that does not know about a command cannot stop it
/// growing.** This raise buys the ratchet its teeth: the surface it now
/// guards is the surface that exists. Every later slice pays it back —
/// PR3 alone retires nine rows.
///
/// **Slice 1 raised these by one, and that is honest rather than a
/// weakening.** `/settings` is a net ADDITION: it absorbs the editor-mode
/// family, but `/vi`, `/emacs`, `/nano` and `/edit-mode` remain reachable
/// as shims, because a removed command that answers "unknown" is worse
/// than the four verbs were. The reduction lands when the deprecation
/// window closes and the shims are retired — four tokens and one command
/// come off then, and this bound comes down with them. Raising a ratchet
/// is allowed exactly when the growth is the plan; it is not allowed to
/// make a surprise go away.
/// **Counted on `Surface::Slash`, not on the register (#2009 PR1).**
///
/// The register GROWS as the cut proceeds — fields, section actions and
/// permanent retirement pointers all keep their rows, because a deleted
/// row loses both the pointer an operator's muscle memory needs and the
/// receipt destination the setting still has. Counting every row would
/// therefore turn the plan into a ratchet violation.
///
/// What may only shrink is what an operator can TYPE at the top level.
///
/// # 42/52 → 43/53: `/models` was never actually retired
///
/// The same shape as PR2's "registering the ghosts": a command an operator
/// can type today, reaching a real handler (`lib.rs`'s dispatch match names
/// `"models"` explicitly), counted by neither number because its row claimed
/// `Surface::Retired("/status models")`. That destination is the rewrite BACK
/// to `/models`, so the row pointed at itself, and the help corpus was pruned
/// to match — leaving `/model <name>` as the only listed model verb and no
/// listed way to see the names. **A ratchet that does not know about a
/// command cannot stop it growing.** This raise makes the guarded surface the
/// surface that exists; it is honest rather than a weakening.
///
/// # 43/53 → 44/55: `/discuss` (alias `/chat`), #2515
///
/// A genuine new command, not a rediscovered one: the clarification gate's
/// own escape hatch was shipped and advertised in the batch text with no
/// slash-registry row at all, so it read as "unknown command" the one time
/// an operator tried it. Registering it — plus its `/chat` alias — is the
/// same honest raise as `/models` above: the guarded surface catching up to
/// the surface that exists.
///
/// # 45/56 → 46/57: `/ocap` proposes THIS session (Step 27.14 / #2679)
///
/// A genuine top-level command, not a field of `/settings`: it reads a
/// capture, proposes an approve.toml, and optionally persists candidates. It
/// is not a subcommand of `/permissions` because `/permissions` renders what
/// is currently granted — `/ocap propose` asks what to GRANT next, from a
/// different source. It is `Receipt::Missing` on purpose: the save writes to
/// the signed `ocap/session-grants.age` store, and a `/ocap propose` journal
/// is still owed (raised alongside the missing-receipt count). Both doors are
/// honest about their own surface.
///
/// # 44/55 → 45/56: operator workspace profiles (Step 27.13)
///
/// `/workspace` is restored as the explicit shortcut to the reviewed operator
/// editor at `/settings workspaces`. The old path read remains `/status
/// workspace`. This is an intentional additional operator entry point, not a
/// model tool or a new settings implementation; both doors use one editor and
/// the signed policy store. Its durable receipt is registered separately.
/// **The register may grow; only the surface may not.**
///
/// Anti-vacuous guard on the ratchet above: if `slash_commands()` ever
/// returned everything, the two numbers would coincide and the ratchet
/// would silently become the old one again — which is exactly the shape
/// the cut needs it not to be.
/// **The exact guard: a new interception SITE forces a registry review.**
///
/// Containment (below) catches a command that disappears. Nothing catches
/// one that APPEARS, because a new `if slash_body == "whatever"` is
/// invisible to a scan that only knows the tokens it was told about. The
/// site count is the proxy that is exact: every top-level command reaches
/// its handler through one of these, so a new one means either a new
/// command or a refactor, and both deserve a look at this file.
/// # PR6 DID free one: 22 → 21
///
/// The `/recall` arm is gone, not redirected. `parse_resume_command` reads
/// `/recall` as the `/resume find` it retired into, so the retired verb
/// runs the replacement's code instead of a second copy — which is what
/// makes the site removable rather than merely renamed. This is the first
/// real consolidation of the cut, and §5's rule is satisfied: a recount
/// says so, rather than a forecast.
///
/// # PR3 did NOT free a site, and says so
///
/// The train predicted the `/status` fold would kill the `/info` site. It
/// did not: a retired READ keeps reading, so the `/status || /info` arm is
/// still there and still needed. §5's site-count honesty rule is explicit
/// that a shared binding survives until its LAST command dies, and that a
/// slice may only lower this number when a real recount says so — so it
/// stays 22, and the site dies with the shims in PR14b.
/// **A command you can type must be listed where commands are listed.**
///
/// `/models` was typeable, dispatched, and absent from `help_lines()` — the
/// ONE corpus `/help` prints and the rich palette parses. So an operator
/// could switch models (`/model <name>` is listed) but had nowhere to learn
/// the names. Nothing failed: the palette's parity test builds its own
/// fixture entries, so the real corpus can lose a row without any test
/// noticing. That is the vacuous shape this guard exists to close.
///
/// A ratchet, not a wall, in the `KNOWN_VIOLATIONS` idiom (`CLAUDE.md`: the
/// mess is fixed by ratchet, not by rewrite). The five below are real and
/// may only go DOWN — document one and delete its row; never add a row to
/// make a new command pass.
/// Anti-vacuous twin: the guard above must actually be able to SEE a gap.
///
/// If `help_lines()` were empty, or the containment check always matched,
/// the ratchet would pass while documenting nothing. So prove the detector
/// detects: a name that is certainly not in the corpus must be reported as
/// missing, and a name that certainly is must not.