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

Function build_walkthrough

atomic-cli/src/commands/triage/project.rs:870–1002  ·  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

868/// prose rationale. No repo access; every input is already pinned by the
869/// report.
870pub(crate) fn build_walkthrough(
871 change_paths: &[(String, Vec<String>)],
872 file_module: &BTreeMap<String, String>,
873 file_deps: &BTreeSet<(String, String)>,
874 task_facts: &[TaskFact],
875) -> Vec<WalkthroughLayer> {
876 // 1. Cluster: layer key → sorted modified paths; path → layer key.
877 let mut layer_files: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
878 let mut path_layer: BTreeMap<String, String> = BTreeMap::new();
879 for (_, paths) in change_paths {
880 for p in paths {
881 let key = layer_key(p, file_module);
882 layer_files
883 .entry(key.clone())
884 .or_default()
885 .insert(p.clone());
886 path_layer.insert(p.clone(), key);
887 }
888 }
889 if layer_files.is_empty() {
890 return Vec::new();
891 }
892
893 // 2. Order: project file → file dependencies onto layers. `a imports b`
894 // means b is more foundational, so the reading-order edge is b → a.
895 // Only edges where BOTH ends are modified files count — the walkthrough
896 // orders the change-set, not the whole codebase.
897 let mut reads_after: BTreeMap<String, BTreeSet<String>> = BTreeMap::new(); // layer → layers it builds on
898 for (from, to) in file_deps {
899 let (Some(from_layer), Some(to_layer)) = (path_layer.get(from), path_layer.get(to)) else {
900 continue;
901 };
902 if from_layer != to_layer {
903 reads_after
904 .entry(from_layer.clone())
905 .or_default()
906 .insert(to_layer.clone());
907 }
908 }
909
910 // Kahn's algorithm over the layer set, always taking the lexicographically
911 // smallest ready layer. A dependency cycle leaves layers with unresolved
912 // in-edges; they are appended in lexicographic order (deterministic, and
913 // their `depends_on` still names the relationship for the reader).
914 let mut remaining: BTreeSet<String> = layer_files.keys().cloned().collect();
915 let mut ordered: Vec<String> = Vec::new();
916 while !remaining.is_empty() {
917 let next = remaining
918 .iter()
919 .find(|l| {
920 reads_after
921 .get(*l)
922 .map(|deps| deps.iter().all(|d| !remaining.contains(d)))
923 .unwrap_or(true)
924 })
925 .or_else(|| remaining.iter().next()) // cycle: break it lexicographically
926 .cloned()
927 .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