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
//! Orphan detection for `alef verify`.
//!
//! A file a backend used to emit and then quietly stopped emitting sits on disk forever: nothing
//! `alef verify` ran before this module existed ever diffed the disk-side alef-marked file set
//! against what the current run's backends would actually produce, so a dropped emit was
//! invisible across every release until a human happened to notice the stale file by hand (see
//! the Java `NodeContext.java`/`HtmlVisitor.java`/`VisitorBridge.java` case this module exists
//! to catch). [`find_orphaned_generated_files`] closes that gap by comparing
//! [`super::helpers::collect_alef_hashes`]'s disk walk (the same one [`super::helpers::verify_walk`]
//! and [`super::helpers::verify_walk_multi`] already use for staleness) against the expected
//! output-path set [`super::helpers::find_missing_and_frozen_generated_files`] already computes
//! from a full in-memory regeneration.
//!
//! This module only ever reports. Deleting an orphan automatically is unrecoverable if the
//! detection is wrong even once, and it can be wrong for reasons that have nothing to do with
//! the file being stale: a create-once seed a backend emits only when absent (`rust-toolchain.toml`,
//! the wasm `.cargo/config.toml` -- see `src/scaffold/mod.rs`'s `rust_toolchain_file` and
//! `src/scaffold/languages/wasm.rs`'s `wasm_cargo_config_file`, both gated on the path already
//! existing), or a tolerated stage
//! failure (`collect_managed_surface`'s `stage_failures`) that made this run's surface incomplete
//! rather than the file genuinely dropped. A missed report leaves a stale file a human eventually
//! notices, exactly the status quo this module improves on; a wrong deletion destroys work with
//! no undo. The asymmetry is the same one `report_disk_scan_candidates`
//! (`src/cli/pipeline/generate/orphans.rs`) already documents for the build-time disk-scan
//! route -- this module is `alef verify`'s counterpart, not a duplicate: it runs over the whole
//! tree `collect_alef_hashes` walks, using the full multi-stage managed surface as ground
//! truth, rather than one backend's own output root. ~keep
use HashSet;
use ;
/// Workspace-root paths a scaffold stage emits **only when the path is absent** (see
/// `src/scaffold/mod.rs`'s `rust_toolchain_file` and `src/scaffold/languages/wasm.rs`'s
/// `wasm_cargo_config_file`, both gated on `!Path::new(<path>).exists()`). Once created, such a
/// file falls outside every later run's
/// in-memory `surface` by design -- it is meant to become user-owned after the first scaffold,
/// so nothing regenerates or reports it missing if deleted either (see `missing_managed_paths`,
/// which reads the same `surface`). Diffing that surface against disk would misreport this exact
/// file as an orphan on every single `alef verify` run for every consumer who has one, which is
/// not a rare edge case -- it is the common case, not a rare one, so it is excluded by name
/// rather than left to read as a false alarm. Narrow and explicit by construction, the same way
/// `UNMARKABLE_ALEF_MANIFESTS` in `src/cli/pipeline/generate/orphans.rs` is: extend only when a
/// new scaffold call site adds the identical `!exists()` gate, verified at the call site first. ~keep
const CREATE_ONCE_SEED_PATHS: & = &;
/// True when `path` (absolute, under `base_dir`) is a known create-once seed -- see
/// [`CREATE_ONCE_SEED_PATHS`].
/// Absolute, display-formatted paths of every alef-marked file under `base_dir` that is not in
/// `managed_paths` -- i.e. a file the current run's backends would not produce today.
///
/// `managed_paths` must be the union, across every crate in a (possibly multi-crate) workspace,
/// of every path that crate's managed surface would touch this run (see
/// [`super::helpers::MissingAndFrozenFiles::managed_paths`]). Unioning across crates before
/// calling this, rather than diffing crate by crate, is required for correctness: a file crate B
/// legitimately owns would look orphaned if only crate A's managed paths were checked against
/// it.
///
/// Only files [`super::helpers::collect_alef_hashes`] itself would return are candidates, so a
/// user-owned file that happens to sit in a generated directory -- no `alef:hash:`/self-marker
/// header, ever -- can never appear here; ownership is decided purely by the marker, the same
/// predicate every other `alef verify` finding uses. A known [`CREATE_ONCE_SEED_PATHS`] entry is
/// excluded even when absent from `managed_paths`, for the reason documented on that constant.
pub
/// Every path a configured post-build step writes unguarded, across every language in
/// `languages` -- see [`crate::core::backend::PostBuildStep::owned_paths`].
///
/// `alef verify` never runs post-build steps: `complete_generated_artifacts` is invoked only
/// from `Commands::Generate`/`Commands::All` (`bin_cli::core_commands`), so a path one of these
/// steps owns can never appear in `collect_managed_surface`'s in-memory surface the way a
/// `GeneratedFile` would. Left out of the set passed to [`find_orphaned_generated_files`], such a
/// path would misreport as an orphan on every single `alef verify` run the moment a post-build
/// step plants an alef-marked file there -- the same false positive `Commands::Generate`'s own
/// orphan sweep (`bin_cli::core_commands`) already avoids by folding this exact union in before
/// its disk-scan diff (see `owned_paths`'s doc for the alef #B incident that guards against). No
/// shipped backend's `owned_paths` returns a marker-carrying path today, so the gap is latent
/// rather than live here too, but closing it keeps `alef verify` correct the moment one does. ~keep
pub