Files
ARCADIA-Studio/harness/experiments/delete-native-f7-20260912/oracle.rs
T

225 lines
13 KiB
Rust

//! Core-only native deletion oracle. No desktop code or actual-derived expectation.
//! removed BASE_ENTRY ACTUAL_ENTRY TARGET_UUID | same BASE_ENTRY ACTUAL_ENTRY | self-check | generate NEW_DIR
use oa_core::{source_project::*, *};
use std::{collections::BTreeMap, fs, path::Path};
fn compile_exact(raw: &SourceBundle) -> CompiledSourceProject {
let result = compile_bundle(raw, ProjectLimits::default()).expect("source admission");
assert_eq!(&result.bundle, raw, "source migration/normalization is not permitted");
validate_candidate(&result.decoded.project).expect("model admission");
result
}
fn load(entry: &Path) -> CompiledSourceProject {
try_load(entry).unwrap_or_else(|error| panic!("{}: {error}", entry.display()))
}
fn try_load(entry: &Path) -> Result<CompiledSourceProject, String> {
let disk = source_files::load_project(entry, ProjectLimits::default())
.map_err(|error| format!("load source project: {error}"))?;
let errors: Vec<_> = disk.diagnostics.iter()
.filter(|issue| issue.diagnostic.severity == validation::Severity::Error).collect();
if !errors.is_empty() {
return Err(format!("source/discovery errors: {errors:?}"));
}
for issue in &disk.diagnostics {
eprintln!("Permitted non-error diagnostic in {}: {:?}", entry.display(), issue);
}
let raw = disk.reachable_bundle();
if disk.bundle != raw {
return Err("detached/invalid discovered buffers are not admitted".into());
}
let compiled = compile_bundle(&raw, ProjectLimits::default())
.map_err(|issues| format!("source admission: {issues:?}"))?;
if compiled.bundle != raw {
return Err("source migration/normalization is not permitted".into());
}
validate_candidate(&compiled.decoded.project)
.map_err(|error| format!("model admission: {error}"))?;
Ok(compiled)
}
fn removed(base: &CompiledSourceProject, target: Id) -> CompiledSourceProject {
assert!(base.decoded.project.contains(target), "target absent from baseline");
let candidate = prepare_command(&base.decoded.project, &Command::Delete(target))
.expect("baseline Delete must be admissible");
assert!(!candidate.project.contains(target), "Delete must remove requested identity");
assert_ne!(candidate.project, base.decoded.project, "Delete must change model");
let result = update_bundle_with_identity_map(base, &candidate.project, &candidate.identity_map,
ProjectLimits::default()).expect("ordinary source patch must be admissible");
assert_eq!(result.decoded.project, candidate.project, "source must exactly represent candidate");
assert_eq!(result.bundle.files.keys().collect::<Vec<_>>(), base.bundle.files.keys().collect::<Vec<_>>(),
"Delete must retain source files, including newly empty files");
result
}
fn same(expected: &CompiledSourceProject, actual: &CompiledSourceProject) -> bool {
expected.decoded.project == actual.decoded.project && expected.bundle == actual.bundle
}
fn check(expected: &CompiledSourceProject, actual: &CompiledSourceProject) {
assert_eq!(actual.decoded.project, expected.decoded.project, "complete Project differs");
assert_eq!(actual.bundle, expected.bundle, "complete SourceBundle differs (paths or exact UTF-8 bytes)");
}
fn id(n: u128) -> Id { Id::from_u128(0xde1e7e00000040008000000000000000 + n) }
fn fixture() -> CompiledSourceProject {
let mut p = Project::empty();
p.id = id(1); p.alias = "delete_oracle".into(); p.name = "Delete oracle".into();
for (n, kind, name, parent) in [
(10, Kind::Activity, "Target work", None),
(11, Kind::Activity, "Untouched work", None),
(20, Kind::Scenario, "Reference scenario", None),
(21, Kind::Lifeline, "Target reference", Some(id(20))),
] {
let mut e = p.make_element(kind, Layer::Oa, name, parent).unwrap();
e.id = id(n); e.alias = format!("delete_{n}"); p.elements.insert(e.id, e);
}
*p.elements.get_mut(&id(21)).unwrap().shared_mut().unwrap() =
SharedBody::Lifeline { represented: Some(id(10)) };
for (view, placement) in [(30, 31), (40, 41)] {
let node = NodePlacement { id: id(placement), alias: format!("node_{placement}"),
element: id(10), position: Position { x: 80., y: 80. },
size: Size { width: 180., height: 90. }, collapsed: false, port_anchors: vec![] };
p.diagrams.insert(id(view), Diagram { id: id(view), alias: format!("view_{view}"),
name: format!("Work view {view}"), description: String::new(), layer: Layer::Oa,
projection: ProjectionKind::Allocation, view_options: Default::default(),
nodes: BTreeMap::from([(node.id, node)]), routes: vec![] });
}
for (n, kind, layer, name, parent) in [
(12, Kind::Entity, Layer::Oa, "OA Dispatch team", None),
(50, Kind::Function, Layer::Sa, "SA Initialize controller", None),
(51, Kind::StateMachine, Layer::Sa, "SA Controller modes", None),
(52, Kind::Region, Layer::Sa, "SA Main region", Some(id(51))),
(53, Kind::State, Layer::Sa, "SA Ready", Some(id(52))),
] {
let mut e = p.make_element(kind, layer, name, parent).unwrap();
e.id = id(n); e.alias = format!("delete_{n}"); p.elements.insert(e.id, e);
}
for (n, kind, name, source, target) in [
(13, RelationKind::Allocation, "OA performs target work", 12, 10),
(54, RelationKind::StateAction, "SA Entry initialize", 53, 50),
] {
let generated = p.relate(kind, name, id(source), id(target));
let mut relation = p.relations.remove(&generated).unwrap();
relation.id = id(n); relation.alias = format!("delete_{n}");
if let RelationBody::StateAction { phase, .. } = &mut relation.body { *phase = StateActionKind::Entry; }
p.relations.insert(relation.id, relation);
}
for (view, placement) in [(30, 32), (40, 42)] {
p.diagrams.get_mut(&id(view)).unwrap().nodes.insert(id(placement), NodePlacement {
id: id(placement), alias: format!("node_{placement}"), element: id(12),
position: Position { x: 40., y: 40. }, size: Size { width: 380., height: 230. },
collapsed: false, port_anchors: vec![],
});
}
let nodes = [(61, 51, 40., 40., 620., 460.), (62, 52, 60., 95., 580., 380.),
(63, 53, 100., 155., 320., 190.)].into_iter().map(|(n, element, x, y, width, height)| {
(id(n), NodePlacement { id: id(n), alias: format!("node_{n}"), element: id(element),
position: Position { x, y }, size: Size { width, height }, collapsed: false, port_anchors: vec![] })
}).collect();
p.diagrams.insert(id(60), Diagram { id: id(60), alias: "sa_modes_view".into(),
name: "SA Controller modes view".into(), description: String::new(), layer: Layer::Sa,
projection: ProjectionKind::StateMachine { machine: id(51) }, view_options: Default::default(),
nodes, routes: vec![] });
validate_candidate(&p).unwrap();
let entry = language::format(&p).replacen("{\n", "{\n include \"notes.arcadia\";\n", 1);
let raw = SourceBundle { entry: "project.arcadia".into(), files: BTreeMap::from([
("project.arcadia".into(), entry),
("notes.arcadia".into(), "// Preserve deletion evidence 雨.\r\n// Exact bytes matter.\r\n".into()),
]) };
let compiled = compile_exact(&raw);
assert_eq!(compiled.decoded.project, p);
compiled
}
fn self_check() {
let base = fixture();
let expected = removed(&base, id(10));
check(&expected, &compile_exact(&expected.bundle));
assert!(!same(&expected, &base), "must reject no-op");
let wrong = removed(&base, id(11));
assert!(!same(&expected, &wrong), "must reject wrong deletion");
let placement = removed(&base, id(31));
assert!(placement.decoded.project.contains(id(10)));
assert!(placement.decoded.project.contains(id(41)));
assert!(!same(&expected, &placement), "placement removal is not semantic deletion");
assert!(!expected.decoded.project.contains(id(31)));
assert!(!expected.decoded.project.contains(id(41)));
assert!(!expected.decoded.project.contains(id(13)), "semantic deletion removes allocation");
assert!(placement.decoded.project.contains(id(13)), "placement removal retains allocation");
let entry_row = removed(&base, id(54));
assert!(entry_row.decoded.project.contains(id(50)), "Entry row removal retains function");
assert!(entry_row.decoded.project.contains(id(53)), "Entry row removal retains state");
assert!(!same(&entry_row, &removed(&base, id(50))), "row removal differs from function deletion");
assert!(matches!(expected.decoded.project.elements[&id(21)].shared(),
Some(SharedBody::Lifeline { represented: None })), "cross-reference must clear");
let extra = prepare_command(&expected.decoded.project,
&Command::Rename(id(11), "Unrelated damage".into())).unwrap();
let unrelated = update_bundle(&expected, &extra.project, ProjectLimits::default()).unwrap();
assert!(!same(&expected, &unrelated), "must reject unrelated semantic change");
for text in ["// Preserve deletion evidence 雨.\n// Exact bytes matter.\n", "// Damaged\r\n"] {
let mut raw = expected.bundle.clone();
raw.files.insert("notes.arcadia".into(), text.into());
let tampered = compile_exact(&raw);
assert_eq!(tampered.decoded.project, expected.decoded.project);
assert!(!same(&expected, &tampered), "must reject source-only changes");
}
let mut missing = expected.bundle.clone(); missing.files.remove("notes.arcadia");
assert!(compile_bundle(&missing, ProjectLimits::default()).is_err());
check(&base, &compile_exact(&base.bundle));
assert!(!same(&base, &expected), "restoration must reject deleted snapshot");
assert_eq!(base.bundle.files["notes.arcadia"], expected.bundle.files["notes.arcadia"]);
disk_self_check(&base);
println!("PASS self-check: exact candidate/source, wrong target, no-op, unrelated changes, placement/model distinction, crossrefs, source-byte falsifiers, disk warning admission and missing-include/syntax rejection");
}
fn disk_self_check(base: &CompiledSourceProject) {
let nonce = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos();
let dir = std::env::temp_dir().join(format!("arcadia-delete-oracle-{}-{nonce}", std::process::id()));
fs::create_dir(&dir).expect("create unique owned disk self-check directory");
for (path, text) in &base.bundle.files { fs::write(dir.join(path), text).unwrap(); }
let entry = dir.join(&base.bundle.entry);
let disk = source_files::load_project(&entry, ProjectLimits::default()).unwrap();
assert!(disk.diagnostics.iter().any(|issue| issue.diagnostic.severity == validation::Severity::Warning),
"fixture must exercise legitimate warning admission");
check(base, &try_load(&entry).expect("valid fixture with completeness warnings must load"));
fs::remove_file(dir.join("notes.arcadia")).unwrap();
assert!(try_load(&entry).is_err(), "missing include must fail disk admission");
fs::write(dir.join("notes.arcadia"), &base.bundle.files["notes.arcadia"]).unwrap();
fs::write(&entry, "this is not valid Arcadia source").unwrap();
assert!(try_load(&entry).is_err(), "malformed source must fail disk admission");
// Remove only exact files created above; no recursive deletion or borrowed fixture mutation.
for path in base.bundle.files.keys() { fs::remove_file(dir.join(path)).unwrap(); }
fs::remove_dir(&dir).unwrap();
}
fn generate(dir: &Path) {
let baseline = fixture();
fs::create_dir(dir).expect("output directory must not already exist; parent must exist");
for (path, text) in &baseline.bundle.files {
fs::write(dir.join(path), text).expect("write new fixture");
}
check(&baseline, &load(&dir.join(&baseline.bundle.entry)));
println!("PASS generated exact fixture {}", dir.display());
for (n, label) in [(10, "OA target activity"), (12, "OA entity"), (13, "OA allocation"),
(30, "first architecture view"), (31, "first activity placement"), (32, "first entity placement"),
(40, "second architecture view"), (41, "second activity placement"), (42, "second entity placement"),
(50, "SA Entry function"), (51, "SA machine"), (52, "SA region"), (53, "SA Ready state"),
(54, "SA Entry row relation"), (60, "SA state view"), (63, "SA Ready placement")] {
println!("{label}: {}", id(n));
}
}
fn main() {
let args: Vec<_> = std::env::args().collect();
match args.get(1).map(String::as_str) {
Some("self-check") if args.len() == 2 => self_check(),
Some("generate") if args.len() == 3 => generate(Path::new(&args[2])),
Some("same") if args.len() == 4 => {
let base = load(Path::new(&args[2])); let actual = load(Path::new(&args[3]));
check(&base, &actual); println!("PASS same: complete Project and exact SourceBundle restored");
}
Some("removed") if args.len() == 5 => {
let base = load(Path::new(&args[2]));
let target: Id = args[4].parse().expect("TARGET_UUID must be a UUID");
// Expected is fully fixed before actual is loaded.
let expected = removed(&base, target);
let actual = load(Path::new(&args[3]));
check(&expected, &actual);
println!("PASS removed {target}: exact baseline-derived Delete candidate and normal source patch");
}
_ => panic!("usage: removed BASE_ENTRY ACTUAL_ENTRY TARGET_UUID | same BASE_ENTRY ACTUAL_ENTRY | self-check | generate NEW_DIR"),
}
}