Files
ARCADIA-Studio/harness/basic-editing-contract.rs
T
Codex e2ecfea69b
Rust checks / check (ubuntu-latest) (push) Canceled after 0s
Rust checks / check (windows-latest) (push) Canceled after 0s
Checkpoint source 7 workflows and in-progress diagram authoring redesign
Preserve current core, source, REST, desktop, recovery, documentation and validation work, including AGENTS.md and the development summary. This is a work-in-progress checkpoint: the new compact palette has outstanding compilation errors and native acceptance; earlier passing gates retain their documented scope.
2026-09-10 10:06:55 -07:00

515 lines
16 KiB
Rust

//! 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>) -> 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,
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<Id>,
}
impl Roles<'_> {
fn fresh(&mut self, candidates: Vec<Id>, 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()
);
}