flowrs-engine/crates/flowrs-core/tests/provenance_edit_paths.rs
flowRS fd958c0547 flowRS engine, cut from flowRS 3451b065
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-02 19:04:39 -05:00

85 lines
3.1 KiB
Rust

//! Every path that writes the IDE's graph says who wrote it (work/provenance).
//!
//! A person's edit in the IDE is theirs only if it goes through `AppState::edit`, which
//! stamps what changed ([`provenance::edit_as`](flowrs_core::graph::provenance::edit_as)).
//! A write that bypasses it — a new button that calls `state.graph.update` — would leave
//! the person's change unattributed, and so unprotected from an agent's edit. flowrs-ui is
//! wasm-only and its tests do not run with the workspace's, so the rule is held here, over
//! its source: every raw write to the graph signal carries, on one of the three lines
//! before it, a `// provenance:` comment saying why it is not a person's edit (a snapshot
//! restored, a file opened, an agent's edit already stamped, layout).
use std::path::{Path, PathBuf};
fn rust_files(dir: &Path, out: &mut Vec<PathBuf>) {
for e in std::fs::read_dir(dir).unwrap().flatten() {
let p = e.path();
if p.is_dir() {
rust_files(&p, out);
} else if p.extension().is_some_and(|x| x == "rs") {
out.push(p);
}
}
}
const WRITES: &[&str] = &[
".graph.set(",
".graph.update(",
".graph.try_update(",
".graph.write(",
".graph.update_untracked(",
".graph.set_untracked(",
".graph.maybe_update(",
".graph.try_set(",
];
#[test]
#[ignore = "reads the editor's source, which is not published here"]
fn every_write_to_the_ides_graph_says_who_wrote_it() {
let ui = Path::new(env!("CARGO_MANIFEST_DIR")).join("../flowrs-ui/src");
let mut files = Vec::new();
rust_files(&ui, &mut files);
files.sort();
assert!(
files.len() > 20,
"flowrs-ui sources not found at {}",
ui.display()
);
let (mut raw, mut person, mut unexplained) = (0usize, 0usize, Vec::new());
for f in &files {
let text = std::fs::read_to_string(f).unwrap();
let lines: Vec<&str> = text.lines().collect();
for (i, l) in lines.iter().enumerate() {
let code = l.split("//").next().unwrap_or("");
person += code.matches("state.edit(").count() + code.matches("self.edit(").count();
if !WRITES.iter().any(|w| code.contains(w)) {
continue;
}
raw += 1;
let said = lines[i.saturating_sub(3)..i]
.iter()
.any(|p| p.trim_start().starts_with("// provenance:"));
if !said {
unexplained.push(format!(
"{}:{}: {}",
f.strip_prefix(&ui).unwrap().display(),
i + 1,
l.trim()
));
}
}
}
assert!(
unexplained.is_empty(),
"graph writes that neither go through AppState::edit nor say why they are not a \
person's edit (`// provenance: …` on a line before):\n{}",
unexplained.join("\n")
);
eprintln!(
"flowrs-ui: {person} person edits through AppState::edit, {raw} other writes, each explained"
);
assert!(
person >= 20,
"{person}: the person's edits no longer go through AppState::edit?"
);
}