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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
//! The alef#436 marked-file drift comparison ("does this already-marked, already-present file
//! still match what this run would produce") -- alef#436's second half, and the home for
//! [`drifted_marked_paths`], the two-tier comparison `bin_cli::helpers::find_missing_and_frozen_
//! generated_files` calls into.
//!
//! # What this has to get right
//!
//! A fresh in-memory render is NOT what ends up on disk. `alef all` writes the render and then
//! reformats it -- `poly fmt --fix`, `cargo fmt --all`, `cargo sort -n -w`, `mix format`, a
//! `[crates.e2e.format]` override -- and only then stamps the hash. So "did this file drift"
//! can only be answered against the render PLUS whatever alef's own pass does to it at that
//! path. Getting the "whatever" wrong in either direction is a real defect:
//!
//! - Model too much formatting and the check reports drift the writer can never clear.
//! - Model too little and it certifies a stale file as fresh.
//!
//! [`crate::cli::pipeline::formatting_owner`] is the single answer to "what formats this path",
//! shared with the writer. This module consumes it; it does not keep its own copy.
//!
//! # The alef#478 defect
//!
//! This module used to hold a second, one-line, wrong copy of that answer: *`poly fmt`, always*,
//! applied to a temp sibling of the real file. In a consumer repo that manufactured **197**
//! drift findings against a tree two consecutive `alef all` runs left byte-identical -- **zero**
//! of the 197 overlapped the set `alef all` actually rewrites. Three independent causes, all
//! measured:
//!
//! 1. **poly's excludes are name-keyed, and a temp sibling escapes them.** The real
//! `crates/<crate>-ffi/Cargo.toml` is excluded by the consumer's `**/Cargo.toml`;
//! `.alef-verify-drift-<rand>.toml` beside it is not. The check compared disk against a
//! taplo-reformatted rendering of a file poly never touches.
//! 2. **Trees poly's discovery prunes.** A gitignored directory, or a bare `e2e/**` glob that
//! (per poly's own documentation) also prunes `src/test/java/io/<org>/e2e/`, where `e2e` is a
//! package name.
//! 3. **Formatters the check modelled not at all** -- the residuals and `[crates.e2e.format]`
//! overrides above. Elixir was the sharpest: poly is excluded from `.ex`/`.exs` on BOTH
//! sides, so the temp copy was a no-op and the check demanded that disk equal the raw
//! emitter output while the writer had run `mix format` over it.
//!
//! Measured against the same consumer: of those 197 paths, poly's own pass reaches **32**; the
//! other **165** it never sees. ~keep
//!
//! # Why `.rs` and `.md` are no longer symmetric (alef#458, alef#465, alef#469, alef#478)
//!
//! Both extensions once took a fast path that skipped the formatter entirely, on the theory
//! that [`crate::cli::pipeline::normalize_content`] reproduces what poly would do to them.
//! That is true for `.rs` and **false for `.md`**, and alef#458 got here by reasoning about
//! implementation symmetry instead of measuring each one.
//!
//! `.rs` keeps the fast path. `normalize_content` runs a REAL `rustfmt` (`format_rust_content`),
//! not an imitation of one. alef#465 also fixed the ordering bug that used to make the
//! prediction wrong on a first run -- `rustfmt.toml` is now written by
//! [`crate::cli::pipeline::write_format_config_prepass`] ahead of the bindings stage, so the
//! first write is already rustfmt-formatted -- and
//! `tests::rust_binding_bytes_on_disk_match_a_fresh_render_with_no_poly` drives a real
//! from-scratch `alef all` with `poly` removed from `PATH` and asserts byte equality. ~keep
//!
//! `.md` loses it whenever poly is present. alef models exactly one rumdl rule: a blank-line cap
//! matched to **MD012** (`normalize_whitespace_with_policy`). The poly config alef itself
//! generates (`scaffold::languages::poly`'s `RUMDL_DISABLE`) **disables MD012** -- so the one
//! rule alef reproduces is the one rule that is switched off, and every rule that actually
//! reshapes alef's markdown is left enabled and unmodelled.
//!
//! Measured in a consumer, by disabling each rule in turn and re-running the real formatter over
//! the raw render: a cold `alef readme` left **11 of 16** generated READMEs differing from
//! committed and a cold `alef docs` **18 of 23** reference pages, and a real
//! `poly fmt --fix --fix-generated` reconciled **all 29** exactly. The responsible rules are
//! **MD032** (blanks around lists -- all 11 READMEs), **MD038** (spaces inside code spans -- 17
//! of the 18 docs pages), **MD022** (blanks around headings -- 2 pages) and **MD040** (1). Note
//! MD032 and MD022 are one emitter defect wearing two rule names: alef emits a heading with no
//! blank line after it, and which rule repairs it depends only on whether a list or prose
//! follows. **Zero** of the 29 differ by MD022 alone, so a prediction written against MD022 --
//! the first and wrong guess at this -- would have missed every README.
//!
//! The writer was a correct fixed point the whole time and this check was wrong about all 29.
//! With poly ABSENT the prediction is exact again -- nothing reformats the file after alef
//! writes it -- so the fast path is kept for that case and only that case.
//!
//! The lasting lesson is why this module no longer enumerates rules at all: the fix is not to
//! model MD032 and MD038 too, it is to stop predicting and ask the real formatter. Any list of
//! rules here would be one poly release away from being wrong again. ~keep
//!
//! # Why the temp files live beside the real file, not in a scratch directory
//!
//! poly resolves `poly.toml` (and each engine's own project markers -- `pyproject.toml`,
//! `tsconfig.json`, ...) by walking up from the target file's own directory. A scratch directory
//! elsewhere would need every one of those mirrored alongside it, which is exactly the
//! "reproduce poly's behaviour" trap this module exists to avoid. A same-directory sibling
//! sharing the real file's extension resolves every one of those configs as the real file would.
//!
//! Staging is now confined to paths [`crate::cli::pipeline::FormattingOwner::Poly`] actually
//! claims. `alef verify` is documented read-only, and on the consumer above the old
//! unconditional version wrote **2156** temp files into the working tree on a single run. ~keep
//!
//! # Why one batched `poly fmt --fix` invocation, not one per file
//!
//! `poly` is a subprocess; spawning one per candidate turns a verify run into hundreds of
//! process spawns. Every sibling temp file is handed to a single
//! [`crate::cli::pipeline::poly_format_strict`] invocation instead. ~keep
use crate;
use crateResolvedCrateConfig;
use ;
/// One file whose fast (`.rs`) prediction does not apply and whose bytes poly does shape,
/// paired with its current disk content and this run's freshly rendered content -- already
/// through [`crate::cli::commands::adopt::managed_outputs`]'s normalization/header pass, i.e.
/// the bytes `write_files` would put on disk BEFORE the post-write `poly fmt --fix` pass.
pub
/// What [`drifted_marked_paths`] was able to say about each candidate, and what it could not.
///
/// Surfaced all the way to `alef verify`'s own report (`bin_cli::core_commands::verify`) so a
/// gap is a loud, counted skip rather than a silent pass -- this repo's dominant defect shape is
/// a check that examines nothing and still reports clean (the `prove-the-check-fired` rule).
/// No skip is ever folded into `compared`: they answer different questions -- "did the check
/// run" and "what did it find" -- and a report that only stated the sum could not tell a clean,
/// fully-examined tree from one this call gave up on. ~keep
pub
/// Compare every `candidates` entry against disk through a real `poly fmt --fix` pass over a
/// same-directory temp copy of its rendered bytes -- see the module doc for why colocated and
/// why batched. Returns the drifted subset (as `full_path.display()` strings, matching every
/// other list in [`super::MissingAndFrozenFiles`]) alongside [`FormatDriftStats`].
///
/// Gates the up-front "is poly even installed" decision through the injected `is_available`
/// rather than reading PATH directly, so the skip-and-count branch is provable without
/// depending on whether the host running the suite happens to have `poly`. There is
/// deliberately no seam for the formatting pass itself once this gate passes -- emulating
/// poly's own output is exactly the prediction this module exists to avoid, so the "poly ran
/// and found real drift" branch is proven against the real binary instead. ~keep
/// Write `content` into a securely-named temp file beside `real_path`, sharing its extension so
/// poly's per-extension engine dispatch treats it identically -- see the module doc for why this
/// must be a same-directory sibling rather than a scratch directory.
///
/// `NamedTempFile` is deliberate: it deletes itself on drop, so a panic, an early return, or an
/// interrupted `alef verify` run cannot leave a stray `.alef-verify-drift-*` file behind in a
/// consumer's working tree the way a manually paired write+remove could. ~keep
/// Absolute paths of every file in `files` that already exists on disk, already carries alef's
/// own provenance marker, and yet no longer matches the bytes this run's fresh render would
/// produce once alef's own formatting pass has had its say.
///
/// THE GAP this closes (alef#436): a public type gaining a field left `alef docs`' rendered API
/// reference pages stale while `alef verify` stayed green, because every other check was
/// structurally blind to it:
///
/// - [`super::stale_among`]'s per-file `alef:hash:` check recomputes a hash of the file's OWN
/// current bytes and compares it to the hash embedded in those same bytes — it catches a
/// hand-edit, never a file that is internally self-consistent but stale relative to a fresh
/// render.
/// - The crate-scoped `inputs_hash` record (`cache::generation_record`,
/// `bin_cli::core_commands::verify::run`) is written only by `alef generate`/`alef all`, never
/// by `alef docs` — so a docs-only regeneration gap never re-stamps it, but neither does
/// leaving docs untouched ever unstamp it.
/// - `missing_managed_paths`/`frozen_managed_paths` only ever ask "does this path exist" and
/// "does it carry a marker" — never "do its bytes match".
///
/// Three tiers, dispatched by [`formatting_owner`] (see the module doc for the alef#478 defect
/// that having a second, private answer to the same question produced):
///
/// - **Nothing reformats it** ([`FormattingOwner::None`]) — the render IS the final bytes, so
/// compare them directly. This is the large majority of a real tree, and the tier the old
/// code was missing entirely: it staged a temp copy and ran poly over paths poly never sees.
/// - **poly reformats it** ([`FormattingOwner::Poly`]) — run the REAL `poly fmt --fix` over the
/// rendered bytes in a same-directory sibling and compare the result. Exact rather than
/// approximate, and immune to poly gaining or losing an engine.
/// - **alef reformats it with something it cannot model** ([`FormattingOwner::Residual`],
/// [`FormattingOwner::E2eOverride`]) — counted into
/// [`FormatDriftStats::skipped_no_faithful_prediction`] and reported, never silently passed.
///
/// `.rs` short-circuits ahead of all three, and `.md` does too when poly is absent -- see
/// [`render_predicts_final_bytes`] and the module doc for why those two are not the same case.
///
/// Deliberately excludes every file `frozen_managed_paths` would already report: an unmarked
/// file is that check's condition, not this one's, and reporting the same withheld write under
/// two headings would describe it as two findings with two remedies. Also excludes
/// [`crate::cli::pipeline::is_base64_binary_output`] paths.
///
/// Reuses [`crate::cli::commands::adopt::managed_outputs`] and
/// [`crate::cli::pipeline::matches_alef_output`] — the exact pairing `frozen_managed_paths`
/// already uses — rather than a bespoke comparison, because of the caveat that pairing already
/// solves: a self-marking backend (pyo3's `lib.rs`, `docs::render`'s HTML-commented pages) or a
/// `generated_header: true` file both legitimately differ from a naive render by exactly the
/// provenance header/hash line, and `matches_alef_output` is the one place that discounts it. ~keep
pub
/// Testable seam for [`drifted_marked_paths`]: resolves poly's availability through
/// `is_available` rather than PATH, so both the counted-skip branch and the `.md` fast path's
/// poly-absent case are provable on a host that does have poly installed. ~keep
/// The roots to ask poly about, matching the roots alef's own formatting pass hands it.
///
/// `base_dir` is what [`crate::cli::pipeline::converge_full_regen_formatting`] formats on a full
/// regen. The e2e output roots are added because `e2e::format::format_language` names each
/// `<output>/<lang>` directory DIRECTLY, and poly's discovery answers differently for a
/// directory it was handed than for the same directory reached by walking down from a parent --
/// a gitignored tree is the ordinary case. Asking about both is what keeps the reader's notion
/// of coverage equal to the writer's rather than merely similar. Non-existent roots are dropped:
/// poly errors on a path that is not there, which would take the whole probe down with it. ~keep
/// Whether [`crate::cli::pipeline::normalize_content`]'s in-memory normalization is a faithful
/// PREDICTION of the bytes `alef generate`/`alef all` actually leave on disk at `path`.
///
/// `.rs`: yes, unconditionally. `normalize_content` runs a real `rustfmt`, not an imitation.
///
/// `.md`: only when poly is ABSENT. With poly present, rumdl reformats the page afterwards and
/// alef models only its MD012 rule -- which the poly config alef itself generates has DISABLED,
/// while leaving MD022 enabled. See the module doc for the measured alef#478 evidence, and for
/// why the two extensions must keep being reasoned about separately rather than kept symmetric.
///
/// Everything else goes to [`formatting_owner`], which answers from the shared policy rather
/// than from an extension list. ~keep
/// Print `alef verify`'s formatted-output drift coverage line.
///
/// Printed unconditionally, on every run including a clean one, for the same reason
/// `VerifyCoverage` reports scan coverage unconditionally: a check that examined nothing must
/// never render identically to one that found a clean tree (the `prove-the-check-fired` rule).
/// Every skip is named with its own remedy, because they differ: install `poly`, make the
/// checkout writable, or accept that alef cannot speak for a path it reformats with a tool it
/// does not model. ~keep
pub