//! Independent native basic-editing fixture and complete source7 oracle. //! generate DIR | check baseline|entity|basic|transition ENTRY use oa_core::{source_project::*, transitions::*, *}; use std::{ collections::{BTreeMap, BTreeSet}, fs, path::Path, }; fn id(n: u128) -> Id { Id::from_u128(0xb451c000000040008000000000000000 + n) } fn seed(p: &mut Project, n: u128, kind: Kind, layer: Layer, name: &str, parent: Option) -> Id { let mut e = p.make_element(kind, layer, name, parent).unwrap(); e.id = id(n); e.alias = format!("basic_{n}"); p.elements.insert(e.id, e); id(n) } fn fixture() -> Project { let mut p = Project::empty(); p.id = id(1); p.alias = "basic_editing".into(); p.name = "Basic rescue architecture".into(); let machine = seed( &mut p, 20, Kind::StateMachine, Layer::Sa, "Decoy operating modes", None, ); let region = seed(&mut p, 22, Kind::Region, Layer::Sa, "Main", Some(machine)); seed(&mut p, 21, Kind::State, Layer::Sa, "Standby", Some(region)); seed(&mut p, 30, Kind::Activity, Layer::Oa, "Report status", None); seed( &mut p, 31, Kind::Capability, Layer::Oa, "Rescue capability", None, ); seed( &mut p, 32, Kind::Function, Layer::Sa, "Unrelated function", None, ); for (n, layer, projection, name) in [ ( 2, Layer::Sa, ProjectionKind::StateMachine { machine }, "A · Operating modes (not an architecture destination)", ), ( 3, Layer::Sa, ProjectionKind::Capabilities, "B · Capabilities (not an allocation view)", ), ( 4, Layer::Oa, ProjectionKind::Allocation, "Operational rescue architecture", ), ( 5, Layer::Sa, ProjectionKind::Allocation, "System rescue architecture", ), ] { p.diagrams.insert( id(n), Diagram { id: id(n), alias: format!("basic_view_{n}"), name: name.into(), description: String::new(), layer, projection, layout: None, routes: vec![], view_options: Default::default(), nodes: BTreeMap::new(), }, ); } execute_batch(&p, &[]).unwrap() } fn bundle(p: &Project) -> CompiledSourceProject { let source = language::format(p).replacen("{\n", "{\n include \"rationale.arcadia\";\n", 1); let bundle = SourceBundle { entry: "project.arcadia".into(), files: BTreeMap::from([ ("project.arcadia".into(), source), ( "rationale.arcadia".into(), "// Preserve this included engineering note: rescue 雨.\r\n".into(), ), ]), }; let result = compile_bundle(&bundle, ProjectLimits::default()).unwrap(); assert_eq!(result.bundle, bundle); assert_eq!(&result.decoded.project, p); result } fn load(entry: &Path) -> CompiledSourceProject { let files = source_files::load_project(entry, ProjectLimits::default()).unwrap(); let raw = files.reachable_bundle(); let result = compile_bundle(&raw, ProjectLimits::default()).unwrap(); assert_eq!( result.bundle, raw, "No source migration or normalization is permitted" ); execute_batch(&result.decoded.project, &[]).unwrap(); result } fn advance(base: &mut CompiledSourceProject, commands: &[Command]) { let expected = execute_batch(&base.decoded.project, commands).unwrap(); *base = update_bundle(base, &expected, ProjectLimits::default()).unwrap(); } struct Roles<'a> { actual: &'a Project, seed: Project, used: BTreeSet, } impl Roles<'_> { fn fresh(&mut self, candidates: Vec, role: &str) -> Id { assert_eq!(candidates.len(), 1, "Exactly one identity for {role}"); let key = candidates[0]; assert_eq!( key.get_version_num(), 4, "{role} must be a native fresh UUIDv4" ); assert!( !self.seed.contains(key), "{role} cannot reuse a seed identity" ); assert!( self.used.insert(key), "Fresh roles must be distinct: {role}" ); key } fn element(&mut self, kind: Kind, layer: Layer, name: &str) -> Id { self.fresh( self.actual .elements .values() .filter(|e| e.kind() == kind && e.layer() == layer && e.name == name) .map(|e| e.id) .collect(), name, ) } fn node(&mut self, diagram: Id, element: Id) -> Id { self.fresh( self.actual.diagrams[&diagram] .nodes .values() .filter(|n| n.element == element) .map(|n| n.id) .collect(), "exact diagram/element placement", ) } fn relation(&mut self, kind: RelationKind, source: Id, target: Id) -> Id { self.fresh( self.actual .relations .values() .filter(|r| r.kind() == kind && r.body.endpoints() == (source, target)) .map(|r| r.id) .collect(), "exact typed relationship endpoints", ) } } fn place(p: &Project, diagram: Id, element: Id, key: Id, x: f32, y: f32) -> Command { Command::CreateNode { diagram, node: NodePlacement { id: key, alias: p.fresh_alias(&format!("view_{}", p.named(element)), "node", key), element, position: Position { x, y }, size: Size { width: 224., height: 94., }, collapsed: false, port_anchors: vec![], }, } } fn create( base: &mut CompiledSourceProject, roles: &mut Roles<'_>, kind: Kind, name: &str, x: f32, y: f32, ) -> Id { let final_name = if name == "Assess incident" { "Assess rescue incident" } else { name }; let key = roles.element(kind, Layer::Oa, final_name); let node = roles.node(id(4), key); let mut element = base .decoded .project .make_element(kind, Layer::Oa, name, None) .unwrap(); element.id = key; let mut scratch = base.decoded.project.clone(); scratch.elements.insert(key, element.clone()); advance( base, &[ Command::CreateElement(element), place(&scratch, id(4), key, node, x, y), ], ); key } fn relation( base: &mut CompiledSourceProject, roles: &mut Roles<'_>, kind: RelationKind, name: &str, source: Id, target: Id, ) { let key = roles.relation(kind, source, target); let p = &base.decoded.project; let record = Relation { id: key, alias: p.fresh_alias(name, "relation", key), name: name.into(), description: String::new(), body: RelationBody::new(kind, source, target), }; advance(base, &[Command::CreateRelation(record)]); } fn expected(mode: &str, actual: &Project) -> CompiledSourceProject { let initial = fixture(); let mut result = bundle(&initial); if mode == "baseline" { return result; } let mut roles = Roles { actual, seed: initial, used: BTreeSet::new(), }; let entity = create( &mut result, &mut roles, Kind::Entity, "Rescue team", 120., 120., ); if mode == "entity" { return result; } create( &mut result, &mut roles, Kind::Actor, "Control centre", 680., 120., ); let activity = create( &mut result, &mut roles, Kind::Activity, "Assess incident", 160., 300., ); let node = roles.node(id(4), id(30)); let command = place(&result.decoded.project, id(4), id(30), node, 680., 300.); advance(&mut result, &[command]); relation( &mut result, &mut roles, RelationKind::Allocation, "Performs", entity, activity, ); relation( &mut result, &mut roles, RelationKind::Interaction, "Interaction: Assess incident → Report status", activity, id(30), ); // Rename retains the creation-time alias; do this after drawing Interaction. let mut renamed = result.decoded.project.elements[&activity].clone(); renamed.name = "Assess rescue incident".into(); advance(&mut result, &[Command::EditElement(activity, renamed)]); if mode == "basic" { return result; } assert_eq!(mode, "transition"); let p = &result.decoded.project; let mut request = TransitionRequest::new(TransitionDirection::OaToSa, vec![entity]); request.scope = DependencyScope::SelectedOnly; request.destination_diagram = Some(id(5)); request.mappings.push(TransitionMapping { source: entity, target: None, target_kind: Some(Kind::SystemActor), }); let plan = plan_transition(p, &request).unwrap(); assert!(plan.conflicts.is_empty(), "{:?}", plan.conflicts); let mut target = apply_transition(p, &plan).unwrap(); let generated = plan .changes .iter() .find(|c| c.source == entity) .unwrap() .target; let counterpart = roles.element(Kind::SystemActor, Layer::Sa, "Rescue team"); reidentify(&mut target, generated, counterpart).unwrap(); let trace = roles.fresh( actual .traces .values() .filter(|t| t.oa == entity && t.sa == counterpart) .map(|t| t.id) .collect(), "counterpart trace", ); let generated_trace = target .traces .values() .find(|t| t.oa == entity && t.sa == counterpart) .unwrap() .id; reidentify(&mut target, generated_trace, trace).unwrap(); let placement = roles.node(id(5), counterpart); let generated_node = target.diagrams[&id(5)] .nodes .values() .find(|n| n.element == counterpart) .unwrap() .id; // The OA placement already owns view_rescue_team. Its SA counterpart's // collision suffix is derived from the new placement UUID, never adopted // from the observed alias or left at the planner's temporary UUID. target .diagrams .get_mut(&id(5)) .unwrap() .nodes .get_mut(&generated_node) .unwrap() .alias = p.fresh_alias("view_Rescue team", "node", placement); reidentify(&mut target, generated_node, placement).unwrap(); assert_eq!( target.diagrams[&id(2)], p.diagrams[&id(2)], "State-machine decoy must remain untouched" ); assert_eq!( target.diagrams[&id(3)], p.diagrams[&id(3)], "Capability decoy must remain untouched" ); result = update_bundle(&result, &target, ProjectLimits::default()).unwrap(); result } fn self_check() { // Role carrier supplies identities only. Deliberately wrong geometry must // never become the expected placement: the oracle predicts literal values. let mut carrier = fixture(); let mut keys = vec![]; for (kind, name) in [ (Kind::Entity, "Rescue team"), (Kind::Actor, "Control centre"), (Kind::Activity, "Assess rescue incident"), ] { let e = carrier.make_element(kind, Layer::Oa, name, None).unwrap(); keys.push(e.id); carrier.elements.insert(e.id, e); carrier.place(id(4), *keys.last().unwrap(), Position { x: 999., y: 999. }); } carrier.place(id(4), id(30), Position { x: 999., y: 999. }); carrier.relate( RelationKind::Allocation, "identity carrier only", keys[0], keys[2], ); carrier.relate( RelationKind::Interaction, "identity carrier only", keys[2], id(30), ); let basic = expected("basic", &carrier); assert_ne!( basic.decoded.project, carrier, "Carrier metadata must not be adopted" ); assert_eq!( expected("basic", &basic.decoded.project).bundle, basic.bundle ); let mut r = TransitionRequest::new(TransitionDirection::OaToSa, vec![keys[0]]); r.mappings.push(TransitionMapping { source: keys[0], target: None, target_kind: Some(Kind::SystemActor), }); r.destination_diagram = Some(id(5)); let plan = plan_transition(&basic.decoded.project, &r).unwrap(); assert!(plan.conflicts.is_empty(), "{:?}", plan.conflicts); let transitioned = apply_transition(&basic.decoded.project, &plan).unwrap(); let oracle = expected("transition", &transitioned); assert_eq!(oracle.decoded.project, transitioned); assert_eq!( expected("transition", &oracle.decoded.project).bundle, oracle.bundle ); r.destination_diagram = Some(id(2)); let invalid = plan_transition(&basic.decoded.project, &r).unwrap(); assert!( !invalid.conflicts.is_empty(), "State-machine decoy must reject a foreign SystemActor" ); // Capabilities legitimately includes participants. It is an unchosen, // non-Allocation view, not an invalid type; explicit selection wins over it. r.destination_diagram = Some(id(3)); assert!( plan_transition(&basic.decoded.project, &r) .unwrap() .conflicts .is_empty() ); let mut altered = transitioned.clone(); altered.elements.get_mut(&keys[1]).unwrap().description = "unrequested modification".into(); assert_ne!(expected("transition", &altered).decoded.project, altered); println!( "PASS self-check: complete basic/transition source fixed points, independent geometry/metadata, state-view refusal and unchanged unchosen Capabilities view" ); } fn main() { assert_eq!(SCHEMA_VERSION, 7); let args: Vec<_> = std::env::args().collect(); if args.get(1).is_some_and(|s| s == "self-check") { self_check(); return; } if args.get(1).is_some_and(|s| s == "generate") { assert_eq!(args.len(), 3); let dir = Path::new(&args[2]); assert!( !dir.exists(), "Generate into a new directory; existing evidence is immutable" ); let p = fixture(); let base = bundle(&p); fs::create_dir_all(dir).unwrap(); for (name, bytes) in base.bundle.files { fs::write(dir.join(name), bytes).unwrap(); } fs::write( dir.join("expected-baseline.json"), serde_json::to_vec_pretty(&p).unwrap(), ) .unwrap(); println!( "PASS generated independent baseline: {} elements, {} diagrams; user objects absent", p.elements.len(), p.diagrams.len() ); return; } assert_eq!( args.len(), 4, "check baseline|entity|basic|transition ENTRY" ); assert_eq!(args[1], "check"); let actual = load(Path::new(&args[3])); let prediction = expected(&args[2], &actual.decoded.project); assert_eq!( actual.decoded.project, prediction.decoded.project, "Complete model differs from typed role-based prediction" ); assert_eq!( actual.bundle, prediction.bundle, "Complete source bundle differs (including comments and included bytes)" ); println!( "PASS {}: complete model and {} exact sources; only fresh role-proven UUIDs borrowed", args[2], actual.bundle.files.len() ); }