//! Design probe only: no production schema changes, no execution semantics. //! Compile directly against an existing oa_core rlib; see the adjacent design. use oa_core::*; use std::collections::{BTreeMap, BTreeSet}; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] struct Endpoint { references: Vec, occurrence: Id, } #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] struct Address { owner: Id, endpoint: Endpoint, } #[derive(Clone, Debug, PartialEq, Eq)] struct Resolved { address: Address, work: Option, } #[derive(Clone, Debug)] enum Purpose { Precedence { guard: String }, Exchange { exchange: Id }, CommonWork { work: Id }, } struct Record { parent: Option, layer: Layer, kind: Kind, reference: Option, work: Option, contribution: bool, } struct Index<'a> { project: &'a Project, records: BTreeMap, cache: BTreeMap, max_depth: usize, cache_capacity: usize, } impl<'a> Index<'a> { fn new(project: &'a Project, max_depth: usize, cache_capacity: usize) -> Self { let records = project .elements .values() .map(|e| { let (reference, work, contribution) = match e.shared() { Some(SharedBody::Occurrence { body, .. }) => match body { OccurrenceBody::SequenceReference { sequence } => (*sequence, None, true), OccurrenceBody::Action { action, .. } => (None, *action, true), OccurrenceBody::Exchange { .. } | OccurrenceBody::Control { .. } => { (None, None, true) } _ => (None, None, false), }, _ => (None, None, false), }; ( e.id, Record { parent: e.parent, layer: e.layer(), kind: e.kind(), reference, work, contribution, }, ) }) .collect(); Self { project, records, cache: BTreeMap::new(), max_depth, cache_capacity, } } fn resolve(&mut self, owner: Id, endpoint: &Endpoint) -> Result { if endpoint.references.len() > self.max_depth { return Err("reference depth limit"); } let address = Address { owner, endpoint: endpoint.clone(), }; if let Some(resolved) = self.cache.get(&address) { return Ok(resolved.clone()); } let root = self.records.get(&owner).ok_or("missing owner")?; if !matches!(root.kind, Kind::Process | Kind::FunctionalChain) { return Err("wrong owner kind"); } let mut current = owner; let mut visited = BTreeSet::from([owner]); for step in &endpoint.references { let reference = self.records.get(step).ok_or("missing path component")?; if reference.parent != Some(current) || reference.layer != root.layer { return Err("path is not direct in this context"); } let next = reference .reference .ok_or("path component is not a resolved sequence reference")?; let target = self .records .get(&next) .ok_or("missing referenced sequence")?; if target.layer != root.layer || target.kind != root.kind { return Err("incompatible referenced sequence"); } if !visited.insert(next) { return Err("recursive sequence reuse"); } current = next; } let leaf = self .records .get(&endpoint.occurrence) .ok_or("missing leaf")?; if leaf.parent != Some(current) || leaf.layer != root.layer || !leaf.contribution { return Err("leaf is not a direct contribution of the resolved owner"); } let resolved = Resolved { address: address.clone(), work: leaf.work, }; // Cache residency cannot change the validity of an authored address. // The production implementation can use LRU; deterministic eviction is // enough to falsify the earlier history-dependent admission mistake. if self.cache_capacity > 0 { if self.cache.len() >= self.cache_capacity { self.cache.pop_first(); } self.cache.insert(address, resolved.clone()); } Ok(resolved) } fn resolve_many( &mut self, owner: Id, endpoints: &[Endpoint], max_unique: usize, ) -> Result, &'static str> { let unique: BTreeSet<_> = endpoints.iter().collect(); if unique.len() > max_unique { return Err("analysis address limit"); } endpoints .iter() .map(|endpoint| self.resolve(owner, endpoint)) .collect() } fn connection( &mut self, owner: Id, source: &Endpoint, target: &Endpoint, purpose: &Purpose, ) -> Result<(), &'static str> { let a = self.resolve(owner, source)?; let b = self.resolve(owner, target)?; if a.address == b.address { return Err("same contextual occurrence"); } match purpose { Purpose::Precedence { guard } => { let _opaque_guard = guard; Ok(()) } Purpose::CommonWork { work } if a.work == Some(*work) && b.work == Some(*work) => { Ok(()) } Purpose::CommonWork { .. } => Err("common work does not match both contributions"), Purpose::Exchange { exchange } => { let relation = self .project .relations .get(exchange) .ok_or("missing exchange")?; if !matches!( relation.kind(), RelationKind::Interaction | RelationKind::FunctionalExchange ) || a.work != Some(relation.source()) || b.work != Some(relation.target()) { return Err("exchange endpoints do not match contextual work"); } Ok(()) } } } } fn id(value: u128) -> Id { Id::from_u128(value) } fn element(p: &mut Project, n: u128, kind: Kind, layer: Layer, parent: Option) -> Id { let mut e = p .make_element(kind, layer, &format!("item_{n}"), parent) .unwrap(); e.id = id(n); p.elements.insert(e.id, e); id(n) } fn occurrence(p: &mut Project, n: u128, layer: Layer, owner: Id, body: OccurrenceBody) -> Id { let key = element(p, n, Kind::Occurrence, layer, Some(owner)); *p.elements.get_mut(&key).unwrap().shared_mut().unwrap() = SharedBody::Occurrence { body, precondition: String::new(), postcondition: String::new(), }; p.elements .get_mut(&owner) .unwrap() .steps_mut() .unwrap() .push(key); key } fn endpoint(path: &[u128], leaf: u128) -> Endpoint { Endpoint { references: path.iter().copied().map(id).collect(), occurrence: id(leaf), } } fn run(layer: Layer) { let mut p = Project::empty(); p.id = id(1000); let chain = if layer == Layer::Oa { Kind::Process } else { Kind::FunctionalChain }; let work_kind = if layer == Layer::Oa { Kind::Activity } else { Kind::Function }; for n in 1..=4 { element(&mut p, n, chain, layer, None); } for n in 10..=13 { element(&mut p, n, work_kind, layer, None); } for (n, owner, work) in [ (20, 2, 10), (21, 2, 11), (22, 2, 12), (23, 3, 11), (24, 1, 13), ] { occurrence( &mut p, n, layer, id(owner), OccurrenceBody::Action { action: Some(id(work)), lifeline: None, }, ); } for (n, owner, target) in [(30, 1, 2), (31, 1, 2), (32, 1, 4), (33, 4, 2), (34, 1, 3)] { occurrence( &mut p, n, layer, id(owner), OccurrenceBody::SequenceReference { sequence: Some(id(target)), }, ); } for (n, source, target) in [(40, 20, 21), (41, 21, 22)] { p.relations.insert( id(n), Relation { id: id(n), alias: format!("link_{n}"), name: format!("link_{n}"), description: String::new(), body: RelationBody::SequenceLink { source: id(source), target: id(target), guard: String::new(), }, }, ); } let exchange = p.relate( if layer == Layer::Oa { RelationKind::Interaction } else { RelationKind::FunctionalExchange }, "X supplies B", id(13), id(11), ); assert!( execute_batch(&p, &[]).is_ok(), "Probe fixture must be admitted by the existing core" ); let before = p.clone(); assert!(!validation::can_connect( &p, RelationKind::SequenceLink, id(24), id(21) )); assert!( execute_batch( &p, &[Command::Connect( RelationKind::SequenceLink, "unqualified".into(), id(24), id(21) )] ) .is_err() ); let (x, r1b, r2b, nested_b, qb) = ( endpoint(&[], 24), endpoint(&[30], 21), endpoint(&[31], 21), endpoint(&[32, 33], 21), endpoint(&[34], 23), ); let mut index = Index::new(&p, 32, 128); let first = index.resolve(id(1), &r1b).unwrap(); let second = index.resolve(id(1), &r2b).unwrap(); let nested = index.resolve(id(1), &nested_b).unwrap(); assert_ne!(first.address, second.address); assert_ne!(first.address, nested.address); assert_eq!(first.work, second.work); assert_eq!(first.work, nested.work); for purpose in [ Purpose::Precedence { guard: "pressure > 400".into(), }, Purpose::Exchange { exchange }, ] { index.connection(id(1), &x, &r1b, &purpose).unwrap(); } index .connection(id(1), &r1b, &qb, &Purpose::CommonWork { work: id(11) }) .unwrap(); assert!( index .connection(id(1), &x, &qb, &Purpose::CommonWork { work: id(11) }) .is_err() ); assert!( index .connection(id(1), &r1b, &x, &Purpose::Exchange { exchange }) .is_err() ); assert!( index .connection( id(1), &r1b, &r1b, &Purpose::Precedence { guard: String::new() } ) .is_err() ); for bad in [ endpoint(&[], 21), endpoint(&[30], 23), endpoint(&[33], 21), endpoint(&[30, 33], 21), endpoint(&[24], 21), endpoint(&[999], 21), ] { assert!( index.resolve(id(1), &bad).is_err(), "accepted invalid path {bad:?}" ); } let mut depth_limited = Index::new(&p, 1, 128); assert_eq!( depth_limited.resolve(id(1), &nested_b), Err("reference depth limit") ); let mut count_limited = Index::new(&p, 32, 1); count_limited.resolve(id(1), &r1b).unwrap(); count_limited.resolve(id(1), &r1b).unwrap(); count_limited.resolve(id(1), &r2b).unwrap(); count_limited.resolve(id(1), &r1b).unwrap(); assert_eq!(count_limited.cache.len(), 1); assert_eq!( count_limited.resolve_many(id(1), &[r1b.clone(), r2b.clone()], 1), Err("analysis address limit") ); assert_eq!( count_limited.resolve_many(id(1), &[r2b.clone(), r1b.clone()], 1), Err("analysis address limit") ); assert_eq!( count_limited .resolve_many(id(1), &[r1b.clone(), r2b.clone()], 2) .unwrap() .len(), 2 ); let mut changed = p.clone(); if let Some(SharedBody::Occurrence { body: OccurrenceBody::SequenceReference { sequence }, .. }) = changed.elements.get_mut(&id(30)).unwrap().shared_mut() { *sequence = Some(id(3)); } assert!(Index::new(&changed, 32, 128).resolve(id(1), &r1b).is_err()); let mut deleted = p.clone(); deleted.elements.remove(&id(30)); assert!(Index::new(&deleted, 32, 128).resolve(id(1), &r1b).is_err()); assert_eq!( p, before, "Address resolution may not mutate shared procedures" ); println!( "{layer:?}: existing foreign link rejected; R1/R2/nested distinct; 3 purposes checked; invalid paths/retarget/delete/limits refused; shared model unchanged" ); } fn main() { run(Layer::Oa); run(Layer::Sa); println!( "PASS: bounded contextual resolver design probe (not production schema/API acceptance)" ); }