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
299
300
301
302
303
304
305
306
307
308
309
310
use *;
/// **The #1965 debt, counted so it can be paid down.**
///
/// Slash commands never reach the receipt path — that is how a round-cap
/// escalation to unlimited left no durable record. This is the size of
/// that hole, and like the sprawl ratchet it may only go DOWN.
///
/// **Counted across EVERY surface, unlike the shrink ratchets.** A mutator
/// does not stop owing a receipt by becoming a settings field or a section
/// action; it only stops being typed. Scoping this to `Surface::Slash`
/// would let the entire debt disappear as the cut proceeds, which is the
/// most tempting wrong answer available here.
///
/// # 24 → 18: the event journal pays six at once (#2085 PR-E2)
///
/// **The largest payment of the cut, and the first one that is not a
/// relocation.** Every reduction before this either moved a setting's
/// state somewhere `apply_and_record` could read a from→to, or found a row
/// that never owed. These six owed, and could not be paid at all, because
/// they are *operations*: a note is appended, a conversation is deleted, a
/// switch is thrown. There is no previous value to record, so no
/// `SettingValue` could ever have been minted for them — §4.4 parked them
/// against the event journal rather than handing them a fabricated
/// baseline, and #2087 landed the chain they were parked against.
///
/// Row by row, with the kind each now records and the route it records it
/// through (`event_receipt`):
///
/// - **`/remember`** → `NoteAppend`. `memory.add_note` appended and left
/// nothing durable behind it. Records the note's SIZE, never its text.
/// - **`/compress` (`/compact`)** → `Compression`. Records only a run that
/// FIRED, with the pipeline's own `how` and the token delta. The two
/// spellings are two events, which is what `via` is for.
/// - **`/undo-lock`** → `Reopen`. #1749's reversal of a decision the
/// harness adjudicated for itself. §7 Q7 named this and the kill-switch
/// as the two events whose absence made the parked set unacceptable.
/// - **`/dock`** → `Kill` on `disable`/`off`, `Grant` on `enable`/`on`.
/// Both directions, because a kill-switch journalled one way reads as
/// still shut forever. `status` records nothing; it is a read.
/// - **`/resume`** → `ConversationOp` for restore, rename and delete,
/// recorded inside `handle_conversation_command` — the one place both
/// doors converge, so a future door cannot bypass it.
/// - **`/rename` (`/name`)** → `ConversationOp`. A DIFFERENT mutator from
/// the one above, not a second door onto it: this retitles the
/// conversation you are IN, and on a conversation with no durable row it
/// creates the row titled — recorded as `title`, not `rename`, because
/// those are not the same fact.
///
/// **`/allow` did NOT flip, and the reason is the interesting one.**
/// #2085 leads with "permission grants", and the obvious row for it is
/// `/allow`. But `/allow` and `/permissions` share ONE arm in `chat.rs`
/// that prints the session's decisions and the audit tail and refuses
/// every other argument — its comment says *"Read-only by design"*, and
/// the code agrees. **The grant is not a slash command at all**: it is
/// minted in `permissions::PromptPermissionGate::record`, at the prompt,
/// which has no row in this register and already writes a FOURTH flat log
/// (`permission-log.jsonl`). Moving that onto the chain is the
/// `settings_receipt`/`denial_journal` migration #2085 records and does
/// not sequence, so it is not this slice's to make.
///
/// The two rows stay `Missing` rather than being reclassified `None_`,
/// which is the one direction a ratchet must be argued for: the plan's own
/// table (§3) routes `/allow` to `/settings permissions allow …` in PR10,
/// where it becomes a real mutator. Clearing the debt now would have to be
/// undone then, and a ratchet that moves down and back up teaches its
/// reader to edit the number. `/conversation` stays for the sibling
/// reason: its mutators redirect today, but its row is the parked pointer
/// §4.4 kept deliberately.
///
/// `EventKind::Grant` is therefore first used by the other ungating there
/// is — `/dock enable` — and not by a permission prompt.
///
/// # 25 → 24: `/posture` pays, and the ledger's exit opens (#2009 PR10c)
///
/// The last of the relocations §5.1 named. `ActivePosture` and
/// `build_posture` moved to `newt_core::posture`, so the value lives where
/// a pure `settings_form::apply` can install it and `apply_and_record` can
/// read a real from→to.
///
/// It was smaller than the ledger implied: `ActivePosture` is plain data
/// over `Caveats`, and `build_posture` already took the skill loader as a
/// CLOSURE — which is what let it move without dragging the skills path
/// with it. Recorded because the ledger's estimate was the thing that made
/// this look like a project.
///
/// # 27 → 26: `/retrieval` was never a mutator (#2009 PR11)
///
/// A truthing reclassification, and the decision doc predicted it: the row
/// was registered `Absorb`/`Missing` as though `/retrieval` set something,
/// but **its only live handler is the nav ledger** — `parse_retrieval`
/// produces a `NavCommand` that renders a view. It writes nothing, so it
/// never owed a receipt, and it is not a `/settings` field either.
///
/// Same shape as `/memory` and `/loadout` in PR3: the debt was never
/// theirs, and saying so needs the argument recorded, not just the number
/// lowered.
///
/// # 28 → 27: `/detail` pays (#2009 PR7b)
///
/// **Back below where the cut started.** The count was 27 when PR1 armed
/// it, rose to 33 as PR2 registered five ghosts that had never been
/// counted, and has been paid down since — by relocation, not by
/// reclassification, every time except PR3's two verified read-only rows.
///
/// `/detail`'s override was a `run_chat` local shared with `/spill`. It
/// lives in core now, so `apply_and_record` can read a real from→to.
///
/// # 29 → 28: `/prompt` pays (#2009 PR5)
///
/// Its state already lived in `NEWT_PROMPT`, so no relocation was needed —
/// what it lacked was a single writer. `/prompt set` open-coded its own
/// `set_var`, which is precisely the "one mutation path is aspirational
/// rather than true" the `/vi` arm's comment warns about. It routes
/// through `apply_and_record` now.
///
/// # 30 → 29: `/mode` pays the same way (#2009 PR4b)
///
/// Same shape as `/markdown` and for the same reason: absorbing it moved
/// `OperatingMode` down to core and the session value out of a `run_chat`
/// local, so `apply_and_record` can finally read a real from→to. The
/// relocation is the payment.
///
/// # 31 → 30: `/markdown` pays, rather than being reclassified (#2009 PR4)
///
/// The two before it came off by argument — they never owed. This one is
/// paid: `/markdown` mutates, still mutates, and now writes a receipt,
/// because absorbing it moved its state out of a `run_chat` local into
/// `session_markdown_mode` where `settings_form::apply_and_record` can
/// read a from→to. **That relocation IS the payment.** A field whose
/// previous value lives in a local can only be recorded as a guess.
///
/// # 33 → 31: two truthing reclassifications, verified (#2009 PR3)
///
/// `/memory` and `/loadout` were both registered `Missing` on a
/// **read-only description** — the doc flagged both as "verify; if it
/// writes nothing, reclassify `None_` with the argument recorded". Read,
/// and recorded here:
///
/// - `/memory` (`chat.rs:3279`) calls `memory.usage()` and prints the
/// compression counters. No store, no filesystem, no config write.
/// - `/loadout` (`chat.rs:5613`) renders a resolution view for
/// `""`/`show` and prints a refusal otherwise. No write on either path.
///
/// **This lowers the debt without paying anything, which is the one
/// direction a ratchet must be argued for rather than just taken.** The
/// argument is that the debt was never theirs: `Missing` means "mutates
/// and records nothing", and neither mutates. A row that cannot write
/// cannot owe a receipt.
///
/// # 28 → 33: five ghosts walk into the count (#2009 PR2)
///
/// `/new` (`/clear`), `/end`, `/restart`, `/start` and `/cd` are shipped,
/// advertised, state-mutating commands that were in NO register — so they
/// were outside this number while owing exactly what it measures. Four
/// finalize a conversation and write `end_reason`; `/cd` moves
/// `session_cwd`. None of them records a receipt.
///
/// **The debt did not grow by five; the instrument stopped under-reading
/// by five.** Per §4.4 these are operations, not settings — they have no
/// prior value for a `from→to` — so they park here, counted, against the
/// event journal (PR-E) rather than being handed a fabricated baseline.
///
/// # 27 → 28: the one declared raise (#2009 §7 Q8)
///
/// `/probe reset` wipes every learned capability — tool conformance,
/// context windows, calibration — and has never been registered. The
/// count was not 27 because the debt was 27; it was 27 because this row
/// was invisible.
///
/// # 18 → 19: `/ocap` proposes this session (#2679)
///
/// `/ocap propose --save` persists candidate approvals to the signed
/// `ocap/session-grants.age` store rather than to the settings or event
/// journal. That is a durable write, but it is NOT a `settings_receipt` /
/// `event_journal` row — the two writers both read the receipt column and
/// neither reads the other's variant, so there is no honest destination here
/// yet. It is therefore `Receipt::Missing` on the same honest basis as
/// `/permissions` and `/mcp`: the state change lands in a store, but not one
/// this instrument counts. Raising the ratchet names the item and says the
/// debt is real rather than hiding it; a real receipt destination (a journal
/// of proposals made) is still owed.
///
/// Q8's recommendation, taken: *"Approve. The alternative is a mutator
/// that stays invisible because registering it would embarrass a number."*
/// Raising a ratchet is allowed exactly when the growth is the plan and
/// the item is named. It is never allowed to make a surprise go away, and
/// the itemization above is what separates the two.
/// **The other half of the ratchet above: a paid row must actually pay.**
///
/// Lowering the debt count is a claim about the CODE, and a claim a doc
/// comment cannot check. This joins the register to the dispatch **both
/// ways**: every row #2085 PR-E2 flipped to `Event` names the
/// `event_receipt` constructor its arm must call, and every `Event` row in
/// the register is named here — so neither a deleted call nor a row
/// promoted without one can pass. Deleting a call turns this red with the
/// token in the message. Six mutators inside `run_chat` are not reachable
/// by a unit test; this is the guard that is, and its constructors carry
/// the kind/vocabulary/route assertions in `event_receipt`'s own tests.
///
/// A ratchet nobody can fail is the whole failure mode #1965 exists to
/// prevent, so the number and this test move together or not at all.
/// A read-only command must not claim to mutate, and vice versa: the
/// receipt field is what `mutates()` reports, so a wrong one silently
/// removes a command from the debt count above.