MCPcopy Create free account
hub / github.com/atomicdotdev/atomic / build_walkthrough

Function build_walkthrough

atomic-cli/src/commands/triage/project.rs:847–979  ·  view source on GitHub ↗

Group the candidate modifications into ordered semantic layers. Pure and deterministic: clusters modified paths by [`layer_key`], orders clusters foundations-first by a Kahn toposort over the module-level projection of `file_deps` (a layer that is imported reads before its importer), breaking ties — and cycles — lexicographically, then attaches the tasks, criteria, and changes that land in each l

(
    change_paths: &[(String, Vec<String>)],
    file_module: &BTreeMap<String, String>,
    file_deps: &BTreeSet<(String, String)>,
    task_facts: &[TaskFact],
)

Source from the content-addressed store, hash-verified

845/// prose rationale. No repo access; every input is already pinned by the
846/// report.
847pub(crate) fn build_walkthrough(
848 change_paths: &[(String, Vec<String>)],
849 file_module: &BTreeMap<String, String>,
850 file_deps: &BTreeSet<(String, String)>,
851 task_facts: &[TaskFact],
852) -> Vec<WalkthroughLayer> {
853 // 1. Cluster: layer key → sorted modified paths; path → layer key.
854 let mut layer_files: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
855 let mut path_layer: BTreeMap<String, String> = BTreeMap::new();
856 for (_, paths) in change_paths {
857 for p in paths {
858 let key = layer_key(p, file_module);
859 layer_files
860 .entry(key.clone())
861 .or_default()
862 .insert(p.clone());
863 path_layer.insert(p.clone(), key);
864 }
865 }
866 if layer_files.is_empty() {
867 return Vec::new();
868 }
869
870 // 2. Order: project file → file dependencies onto layers. `a imports b`
871 // means b is more foundational, so the reading-order edge is b → a.
872 // Only edges where BOTH ends are modified files count — the walkthrough
873 // orders the change-set, not the whole codebase.
874 let mut reads_after: BTreeMap<String, BTreeSet<String>> = BTreeMap::new(); // layer → layers it builds on
875 for (from, to) in file_deps {
876 let (Some(from_layer), Some(to_layer)) = (path_layer.get(from), path_layer.get(to)) else {
877 continue;
878 };
879 if from_layer != to_layer {
880 reads_after
881 .entry(from_layer.clone())
882 .or_default()
883 .insert(to_layer.clone());
884 }
885 }
886
887 // Kahn's algorithm over the layer set, always taking the lexicographically
888 // smallest ready layer. A dependency cycle leaves layers with unresolved
889 // in-edges; they are appended in lexicographic order (deterministic, and
890 // their `depends_on` still names the relationship for the reader).
891 let mut remaining: BTreeSet<String> = layer_files.keys().cloned().collect();
892 let mut ordered: Vec<String> = Vec::new();
893 while !remaining.is_empty() {
894 let next = remaining
895 .iter()
896 .find(|l| {
897 reads_after
898 .get(*l)
899 .map(|deps| deps.iter().all(|d| !remaining.contains(d)))
900 .unwrap_or(true)
901 })
902 .or_else(|| remaining.iter().next()) // cycle: break it lexicographically
903 .cloned()
904 .expect("remaining is non-empty");

Calls 14

layer_keyFunction · 0.85
task_headlineFunction · 0.85
extendMethod · 0.80
getMethod · 0.65
removeMethod · 0.65
insertMethod · 0.45
cloneMethod · 0.45
is_emptyMethod · 0.45
iterMethod · 0.45
allMethod · 0.45
containsMethod · 0.45
nextMethod · 0.45