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
//! 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.
//!
//! `.rs` and `.md` predict `alef all`'s final bytes in memory (`normalize_content`'s real
//! `rustfmt` pass, and a blank-line policy deliberately matched to poly's `rumdl`, respectively
//! -- see [`render_predicts_final_bytes`]'s doc for why those two, and only those two, are
//! trustworthy predictions today). Every other poly-formatted extension has no such prediction:
//! `poly fmt --fix` (or whichever native tool poly delegates to when present -- `rubocop`,
//! `php-cs-fixer`, `styler`, ...) can reshape a file's bytes after `write_files` computes its
//! `alef:hash:`, and `normalize_content` has no emulation of any of those engines. The issue
//! comment on alef#436 considered and rejected predicting them byte-for-byte: growing a
//! per-language emulation of an engine alef does not own is a maintenance trap that grows every
//! time poly adds, drops, or reconfigures an engine.
//!
//! Instead, this module runs the REAL `poly fmt --fix` over the freshly rendered bytes and
//! compares the result to disk -- exact rather than approximate, and immune to poly gaining or
//! losing an engine for a given extension: a language poly has no engine for today (or has
//! deliberately excluded, like `.ex`/`.exs`/`.cs` -- see `cli::pipeline::format`'s
//! `POLY_ELIXIR_EXCLUDE_GLOBS`/`POLY_CSHARP_EXCLUDE_GLOBS`) degrades to a plain content
//! comparison for free, because running `poly fmt --fix` on such a file is a no-op.
//!
//! # 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 config files mirrored alongside it to format
//! identically to the real tree, which is exactly the "reproduce poly's behaviour" trap this
//! module exists to avoid. A same-directory sibling, sharing the real file's extension so
//! poly's per-extension engine dispatch treats it identically, resolves every one of those
//! configs exactly as the real file would.
//!
//! # Why one batched `poly fmt --fix` invocation, not one per file
//!
//! `poly` is a subprocess; spawning one per candidate file turns an `alef verify` run with a few
//! hundred generated non-Rust/Markdown files into a few hundred process spawns. Every sibling
//! temp file this module creates is handed to a single [`crate::cli::pipeline::poly_format_strict`]
//! invocation instead. ~keep
use ;
/// One file whose fast (`.rs`/`.md`) prediction does not apply, 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 `alef all`'s post-write `poly fmt --fix` pass touches
/// them.
pub
/// How many candidates [`real_formatter_drift`] actually compared through a real `poly fmt`
/// pass, versus how many it had to skip because `poly` is not installed on this machine.
///
/// Surfaced all the way to `alef verify`'s own report (`bin_cli::core_commands::verify`) so a
/// missing formatter 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). `skipped_missing_formatter` is never folded into `compared`:
/// the two 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 entirely. ~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`].
pub
/// Testable seam for [`real_formatter_drift`]: gates the up-front "is poly even installed"
/// decision through `is_available` instead of PATH, 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, skipping itself when it
/// is absent (see this module's own tests for that pattern). ~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.
///
/// 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 check `alef verify` ran
/// 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: `alef generate` re-stamps the very baseline this
/// check would need to catch the drift, from a run that never touched a single docs page.
/// - `missing_managed_paths`/`frozen_managed_paths` only ever ask "does this path exist" and
/// "does it carry a marker" — never "do its bytes match", so a marked, present, stale file is
/// invisible to both.
///
/// Two-tier comparison, not a single one scoped to `.rs`/`.md`: an earlier version of this check
/// used ONLY the fast in-memory prediction below ([`render_predicts_final_bytes`]) and, when a
/// prior attempt widened that prediction to every extension, broke
/// `verify_reports_an_incomplete_generation_run_instead_of_ordinary_staleness` and two sibling
/// tests, all three asserting the established, pre-existing contract "a completed `alef all` run
/// leaves a tree `alef verify` reports clean." The false positives were `pyproject.toml`,
/// `poly.toml` (TOML, via poly's `taplo`) and a generated `.py` file (via poly's `ruff`): `alef
/// all`'s "Formatting generated files..." stage runs `poly fmt --fix`
/// (`cli::pipeline::format::poly_format`) over the paths it just wrote, AFTER
/// `collect_managed_surface`'s in-memory render is computed, and that external reformatting pass
/// changes bytes `normalize_content` never claims to predict. `normalize_content` only reproduces
/// two of poly's engines — `rustfmt` for `.rs` (`format_rust_content`) and a blank-line policy for
/// `.md` deliberately matched to poly's `rumdl` — so those two extensions are the only ones where
/// an in-memory render is a faithful PREDICTION of the bytes `alef all` actually leaves on disk.
///
/// Rather than growing that prediction language by language (the framing alef#436's own issue
/// comment considered and rejected -- a per-engine emulation of a formatter alef does not own,
/// and does not control the evolution of, is a maintenance trap that grows every time poly adds,
/// drops, or reconfigures an engine), every other marked, existing file is instead compared
/// through [`real_formatter_drift`]: the freshly rendered content is run through a REAL `poly fmt
/// --fix` pass and the result is compared to disk. This is exact rather than approximate, and it
/// degrades safely for an extension poly has no engine for (or deliberately excludes, like
/// `.ex`/`.exs`/`.cs`): running `poly fmt --fix` on such a file is a no-op, so the comparison
/// reduces to plain content equality -- the same answer `normalize_content` would have given, for
/// free, with no per-extension special-casing required here. See this module's own doc for the
/// full mechanism, including why the temp copies it stages must be same-directory siblings of the
/// real file rather than living in a scratch directory.
///
/// A missing `poly` binary is not silently treated as "no drift": [`FormatDriftStats`] counts
/// every candidate this call had to skip instead, and `alef verify`
/// (`bin_cli::core_commands::verify::run`) reports that count on every run -- see the
/// `prove-the-check-fired` rule for why a check that examined nothing must never look like one
/// that passed.
///
/// Deliberately excludes every file `frozen_managed_paths` would already report: an unmarked
/// file is that check's condition, not this one's (this function's own `content_has_alef_marker`
/// guard below is what enforces the split), and reporting the same withheld write under two
/// headings would describe it as two different findings with two different remedies. Also
/// excludes [`crate::cli::pipeline::is_base64_binary_output`] paths: poly fmt has no business
/// reformatting a binary payload, and no such path was ever in scope before this check existed
/// either.
///
/// Reuses [`crate::cli::commands::adopt::managed_outputs`] and
/// [`crate::cli::pipeline::matches_alef_output`] — the exact pairing `frozen_managed_paths`
/// already uses for its own `drifted` field — rather than a bespoke comparison, specifically
/// 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. A hand-rolled byte comparison here would report permanent
/// drift for every one of those files. ~keep
pub
/// 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` -- the fast
/// path [`drifted_marked_paths`] takes for `.rs`/`.md`. Every other extension instead goes
/// through [`real_formatter_drift`], which runs the real formatter rather than predicting it --
/// see this module's own doc and [`drifted_marked_paths`]'s doc for the measured evidence that
/// only these two extensions can be predicted honestly today. ~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).
/// Neither skip is silent -- each one means this run could not tell whether that many
/// non-`.rs`/`.md` generated files are stale, which is a real gap in what the run proved, not a
/// "nothing to report" outcome. The two skips are reported apart because their remedies differ:
/// install `poly`, versus make the checkout writable. ~keep
pub