Files
ARCADIA-Studio/harness/child-work-native-contract.rs

157 lines
12 KiB
Rust

//! Native issue4 fixture/oracle. generate DIR | check oa|sa STAGE ENTRY
//! snapshot ENTRY DIR | compare ENTRY DIR | self-check
use oa_core::{source_project::*, *};
use std::{collections::{BTreeMap, BTreeSet}, fs, path::Path};
fn id(n: u128) -> Id { Id::from_u128(0xc1140000000040008000000000000000 + n) }
fn work(layer: Layer) -> Kind { if layer == Layer::Oa { Kind::Activity } else { Kind::Function } }
fn fixture(layer: Layer) -> Project {
let mut p = Project::empty();
p.id = id(1); p.alias = "child_work_native".into(); p.name = format!("Issue4 {layer:?} child work");
for (n, kind, name) in [(10, if layer == Layer::Oa { Kind::Entity } else { Kind::System }, "Performer"), (11, work(layer), "Parent work")] {
p.elements.insert(id(n), Element { id: id(n), alias: format!("seed_{n}"), name: name.into(), description: format!("Preserve original {name}"), parent: None, body: ElementBody::new(kind, layer).unwrap() });
}
p.relations.insert(id(20), Relation { id:id(20), alias:"original_allocation".into(), name:"Original responsibility".into(), description:"Preserve allocation engineering rationale 雨".into(), body:RelationBody::Allocation { source:id(10), target:id(11) } });
if layer == Layer::Oa {
for n in [12,13] {
p.elements.insert(id(n), Element { id:id(n), alias:format!("role_{n}"), name:format!("Responsibility role {n}"), description:"Preserve role".into(), parent:None, body:ElementBody::new(Kind::Role, layer).unwrap() });
for (r, kind, source, target) in [(n+10,RelationKind::RoleAssignment,id(n),id(11)),(n+20,RelationKind::RoleRealization,id(10),id(n))] {
p.relations.insert(id(r), Relation { id:id(r), alias:format!("responsibility_{r}"), name:format!("Role link {r}"), description:format!("Retain role link metadata {r}"), body:RelationBody::new(kind,source,target) });
}
}
}
for (index, owner) in [id(20),id(22),id(23)].into_iter().filter(|owner| p.relations.contains_key(owner)).enumerate() {
let key=id(50+index as u128);
p.artifact_links.insert(key, ArtifactLink { id:key, alias:format!("evidence_{index}"), name:format!("Responsibility evidence {index}"), description:"Keep owner identity and evidence metadata".into(), owner, relation:ArtifactRelation::References, uri:format!("https://example.invalid/evidence/{index}"), revision:Some("review-7".into()), fragment:Some("assignment".into()) });
}
p.diagrams.insert(id(2), Diagram { id:id(2), alias:"allocation_view".into(), name:format!("{layer:?} allocated work"), description:"Native child-work fixture".into(), layer, projection:ProjectionKind::Allocation, layout:None, routes:vec![], view_options:Default::default(), nodes:BTreeMap::new() });
for (n,element,x,y,collapsed) in [(40,id(10),60.,60.,false),(41,id(11),84.,124.,true)] {
p.diagrams.get_mut(&id(2)).unwrap().nodes.insert(id(n), NodePlacement { id:id(n), alias:format!("seed_view_{n}"), element, position:Position{x,y}, size:Size{width:224.,height:94.}, collapsed, port_anchors:vec![] });
}
execute_batch(&p,&[]).unwrap()
}
fn bundle(p:&Project)->CompiledSourceProject {
let source=language::format(p).replacen("{\n","{\n include \"rationale.arcadia\";\n",1);
let raw=SourceBundle { entry:"project.arcadia".into(), files:BTreeMap::from([("project.arcadia".into(),source),("rationale.arcadia".into(),"// Preserve included note bytes: responsibility 雨.\r\n".into())]) };
let result=compile_bundle(&raw,ProjectLimits::default()).unwrap();
assert_eq!(result.bundle,raw); assert_eq!(&result.decoded.project,p); result
}
fn load(entry:&Path)->CompiledSourceProject {
let raw=source_files::load_project(entry,ProjectLimits::default()).unwrap().reachable_bundle();
let result=compile_bundle(&raw,ProjectLimits::default()).unwrap();
assert_eq!(result.bundle,raw,"No source normalization permitted");
execute_batch(&result.decoded.project,&[]).unwrap(); result
}
fn advance(base:&mut CompiledSourceProject,commands:Vec<Command>) {
let expected=execute_batch(&base.decoded.project,&commands).unwrap();
*base=update_bundle(base,&expected,ProjectLimits::default()).unwrap();
}
#[derive(Clone, Copy)]
struct Keys { children:[Id;3], nodes:[Id;3], allocation:Id }
fn expected(layer:Layer,stage:usize,k:Keys)->CompiledSourceProject {
assert!(stage<=3);
let mut base=bundle(&fixture(layer));
for step in 0..stage {
let p=&base.decoded.project;
let key=k.children[step];
let name=format!("New {}",work(layer).label().to_lowercase());
let element=Element { id:key, alias:p.fresh_alias(&name,"element",key), name, description:String::new(), parent:Some(if step==2 { k.children[0] } else { id(11) }), body:ElementBody::new(work(layer),layer).unwrap() };
let mut commands=vec![Command::CreateElement(element.clone())];
if step==1 {
commands.push(Command::CreateRelation(Relation { id:k.allocation, alias:p.fresh_alias("Performs","relation",k.allocation), name:"Performs".into(), description:String::new(), body:RelationBody::Allocation { source:id(10),target:key } }));
} else {
for original in p.relations.values().filter(|r| [id(20),id(22),id(23)].contains(&r.id)) {
let mut r=original.clone();
match &mut r.body { RelationBody::Allocation{target,..}|RelationBody::RoleAssignment{target,..}=>*target=key,_=>unreachable!() }
commands.push(Command::EditRelation(r.id,r));
}
}
if step==0 {
let mut node=p.diagrams[&id(2)].nodes[&id(41)].clone(); node.collapsed=false;
commands.push(Command::EditNode{diagram:id(2),original:node.id,node});
}
let mut with_element=p.clone(); with_element.elements.insert(key,element);
let (x,y)=[(108.,188.),(108.,298.),(132.,252.)][step];
commands.push(Command::CreateNode { diagram:id(2), node:NodePlacement { id:k.nodes[step], alias:with_element.fresh_alias(&format!("view_{}",with_element.named(key)),"node",k.nodes[step]), element:key,position:Position{x,y},size:Size{width:224.,height:94.},collapsed:false,port_anchors:vec![] } });
if step==2 {
// Grandchild expands first child to bottom370; sibling follows at394
// (24px reflow gap), independently predicted for this fixed fixture.
let mut sibling=p.diagrams[&id(2)].nodes[&k.nodes[1]].clone(); sibling.position.y=394.;
commands.push(Command::EditNode{diagram:id(2),original:sibling.id,node:sibling});
}
advance(&mut base,commands);
}
base
}
fn only(ids:Vec<Id>,role:&str)->Id { assert_eq!(ids.len(),1,"Exactly one {role}"); ids[0] }
fn admit(actual:&Project,layer:Layer,stage:usize)->Keys {
let mut k=Keys{children:[Id::nil();3],nodes:[Id::nil();3],allocation:Id::nil()};
if stage==0 { return k; }
let target=actual.relations[&id(20)].target();
k.children[0]=if stage==3 { actual.elements[&target].parent.unwrap() } else { target };
if stage>=2 {
k.children[1]=only(actual.elements.values().filter(|e|e.parent==Some(id(11))&&e.id!=k.children[0]&&e.kind()==work(layer)).map(|e|e.id).collect(),"second child");
k.allocation=only(actual.relations.values().filter(|r|r.kind()==RelationKind::Allocation&&r.source()==id(10)&&r.target()==k.children[1]).map(|r|r.id).collect(),"new sibling allocation");
}
if stage==3 { k.children[2]=target; }
for step in 0..stage {
let e=&actual.elements[&k.children[step]];
assert_eq!(e.kind(),work(layer)); assert_eq!(e.layer(),layer);
assert_eq!(e.parent,Some(if step==2{k.children[0]}else{id(11)}));
k.nodes[step]=only(actual.diagrams[&id(2)].nodes.values().filter(|n|n.element==e.id).map(|n|n.id).collect(),"child placement");
}
let seed=fixture(layer); let mut used=BTreeSet::new();
for key in k.children[..stage].iter().chain(&k.nodes[..stage]).copied().chain((stage>=2).then_some(k.allocation)) {
assert_eq!(key.get_version_num(),4,"Native fresh UUIDv4 required");
assert!(!seed.contains(key)&&used.insert(key),"Fresh distinct role identity required");
}
k
}
fn check(actual:&CompiledSourceProject,layer:Layer,stage:usize) {
let prediction=expected(layer,stage,admit(&actual.decoded.project,layer,stage));
assert_eq!(actual.decoded.project,prediction.decoded.project,"Complete typed model mismatch, including placements and references");
assert_eq!(actual.bundle,prediction.bundle,"Exact source mismatch, including included note bytes");
}
fn history(actual:&CompiledSourceProject,layer:Layer,stage:usize,first:&CompiledSourceProject,second:Option<&CompiledSourceProject>) {
check(first,layer,1);
let first_keys=admit(&first.decoded.project,layer,1);
let current_keys=admit(&actual.decoded.project,layer,stage);
assert_eq!(current_keys.children[0],first_keys.children[0],"First child identity must match original first-commit carrier");
assert_eq!(current_keys.nodes[0],first_keys.nodes[0],"First placement identity must match original first-commit carrier");
if stage==3 {
let second=second.expect("Original second-commit carrier required");
check(second,layer,2);
history(second,layer,2,first,None);
let second_keys=admit(&second.decoded.project,layer,2);
assert_eq!(current_keys.children[1],second_keys.children[1],"Sibling identity must persist");
assert_eq!(current_keys.nodes[1],second_keys.nodes[1],"Sibling placement identity must persist");
assert_eq!(current_keys.allocation,second_keys.allocation,"Sibling allocation identity must persist");
}
}
fn write_bundle(dir:&Path,raw:&SourceBundle) {
assert!(!dir.exists(),"Use a new evidence directory"); fs::create_dir_all(dir).unwrap();
for (name,bytes) in &raw.files { let path=dir.join(name); if let Some(parent)=path.parent(){fs::create_dir_all(parent).unwrap();} fs::write(path,bytes).unwrap(); }
}
fn main() {
assert_eq!(SCHEMA_VERSION,7);
let args:Vec<_>=std::env::args().collect();
match args.get(1).map(String::as_str) {
Some("generate")=>{assert_eq!(args.len(),3);let dir=Path::new(&args[2]);assert!(!dir.exists()); for (name,layer) in [("oa",Layer::Oa),("sa",Layer::Sa)] {write_bundle(&dir.join(name),&bundle(&fixture(layer)).bundle);} println!("PASS deterministic OA/SA native fixtures generated");}
Some("check")=>{
let layer=match args[2].as_str(){"oa"=>Layer::Oa,"sa"=>Layer::Sa,_=>panic!("oa|sa")};
let stage=args[3].parse::<usize>().unwrap();assert!(stage<=3);
assert_eq!(args.len(),5+stage.saturating_sub(1),"Stages2/3 require original stage1/stage2 checkpoint directories");
let actual=load(Path::new(&args[4]));check(&actual,layer,stage);
if stage>=2 {
let first=load(&Path::new(&args[5]).join("project.arcadia"));
let second=if stage==3 {Some(load(&Path::new(&args[6]).join("project.arcadia")))} else {None};
history(&actual,layer,stage,&first,second.as_ref());
}
println!("PASS complete model, exact source and pinned history stage {stage}");
}
Some("snapshot")=>{assert_eq!(args.len(),4);let actual=load(Path::new(&args[2]));write_bundle(Path::new(&args[3]),&actual.bundle);println!("PASS immutable source checkpoint");}
Some("compare")=>{assert_eq!(args.len(),4);let actual=load(Path::new(&args[2]));let checkpoint=load(&Path::new(&args[3]).join(&actual.bundle.entry));assert_eq!(actual.bundle,checkpoint.bundle,"Saved reachable source bytes changed");assert_eq!(actual.decoded.project,checkpoint.decoded.project);println!("PASS exact checkpoint bytes and model");}
Some("self-check")=>{for layer in [Layer::Oa,Layer::Sa]{let keys=Keys{children:std::array::from_fn(|_|Id::new_v4()),nodes:std::array::from_fn(|_|Id::new_v4()),allocation:Id::new_v4()};for stage in 0..=3 {let candidate=expected(layer,stage,keys);check(&candidate,layer,stage);if stage>=2 {history(&candidate,layer,stage,&expected(layer,1,keys),(stage==3).then(||expected(layer,2,keys)).as_ref());}let mut altered=candidate.decoded.project.clone();altered.elements.get_mut(&id(11)).unwrap().description="Unrequested mutation".into();assert_ne!(expected(layer,stage,admit(&altered,layer,stage)).decoded.project,altered);}}println!("PASS oracle fixed points and unrelated-mutation discriminator");}
_=>panic!("generate DIR | check oa|sa STAGE ENTRY [STAGE1DIR] [STAGE2DIR] | snapshot ENTRY DIR | compare ENTRY DIR | self-check"),
}
}