Complete source 6 engineering workflows and preserve release validation evidence
This commit is contained in:
@@ -0,0 +1,210 @@
|
||||
use super::*;
|
||||
|
||||
fn port_id(choice: &CommunicationPortChoice) -> Id {
|
||||
match choice {
|
||||
CommunicationPortChoice::Reuse { port } => *port,
|
||||
CommunicationPortChoice::Create { id, .. } => *id,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn prepare_ports(
|
||||
original: &Project,
|
||||
working: &mut Project,
|
||||
targets: &BTreeMap<Id, Id>,
|
||||
plan: &mut TransitionPlan,
|
||||
) -> BTreeMap<Id, (Id, Id)> {
|
||||
let mut pairs = BTreeMap::new();
|
||||
for decision in plan.request.communication_endpoints.clone() {
|
||||
let result = (|| -> Result<(Id, Id), String> {
|
||||
if plan.request.direction != TransitionDirection::OaToSa {
|
||||
return Err(
|
||||
"SA endpoint choices apply only to OA→SA communication transition.".into(),
|
||||
);
|
||||
}
|
||||
let means = original
|
||||
.relations
|
||||
.get(&decision.source)
|
||||
.filter(|r| r.kind() == RelationKind::CommunicationMean)
|
||||
.ok_or("Endpoint decision must select an OA communication means.")?;
|
||||
if !plan.request.sources.contains(&means.id) && !targets.contains_key(&means.id) {
|
||||
return Err("Endpoint decision is outside this transition selection.".into());
|
||||
}
|
||||
if pairs.contains_key(&decision.source) {
|
||||
return Err("Choose one component-port pair per communication means.".into());
|
||||
}
|
||||
let mut pending = vec![];
|
||||
for (participant, choice) in [
|
||||
(means.source(), &decision.source_port),
|
||||
(means.target(), &decision.target_port),
|
||||
] {
|
||||
let owner = targets.get(&participant).copied().ok_or("Review each OA participant's SA counterpart before selecting its component port.")?;
|
||||
if working.elements.get(&owner).is_some_and(|e| {
|
||||
e.layer() != Layer::Sa || !matches!(e.kind(), Kind::System | Kind::SystemActor)
|
||||
}) {
|
||||
return Err(
|
||||
"Component-port owner must be the reviewed SA participant counterpart."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
match choice {
|
||||
CommunicationPortChoice::Reuse { port } => {
|
||||
let e = working
|
||||
.elements
|
||||
.get(port)
|
||||
.ok_or("Selected component port does not exist.")?;
|
||||
if e.parent != Some(owner)
|
||||
|| e.layer() != Layer::Sa
|
||||
|| !matches!(
|
||||
crate::ports::port_contract(e),
|
||||
Some(PortContract::Component { .. })
|
||||
)
|
||||
{
|
||||
return Err("Selected component port must belong to the reviewed SA participant and have an explicit component contract.".into());
|
||||
}
|
||||
}
|
||||
CommunicationPortChoice::Create {
|
||||
id,
|
||||
alias,
|
||||
name,
|
||||
description,
|
||||
kind,
|
||||
orientation,
|
||||
} => {
|
||||
if id.is_nil()
|
||||
|| working.contains(*id)
|
||||
|| pending.iter().any(|e: &Element| e.id == *id)
|
||||
{
|
||||
return Err("New component-port identity must be unused; choose Reuse for an existing port.".into());
|
||||
}
|
||||
pending.push(Element {
|
||||
id: *id,
|
||||
alias: alias.clone(),
|
||||
name: name.clone(),
|
||||
description: description.clone(),
|
||||
parent: Some(owner),
|
||||
body: ElementBody::shared_in(
|
||||
Layer::Sa,
|
||||
SharedBody::Port {
|
||||
contract: PortContract::Component {
|
||||
kind: *kind,
|
||||
orientation: *orientation,
|
||||
},
|
||||
legacy: None,
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
let pair = (
|
||||
port_id(&decision.source_port),
|
||||
port_id(&decision.target_port),
|
||||
);
|
||||
if pair.0 == pair.1 || targets.get(&means.source()) == targets.get(&means.target()) {
|
||||
return Err("Communication counterparts must have different participant owners; review the participant boundary mapping.".into());
|
||||
}
|
||||
for e in pending {
|
||||
working.elements.insert(e.id, e.clone());
|
||||
plan.commands.push(Command::CreateElement(e));
|
||||
}
|
||||
Ok(pair)
|
||||
})();
|
||||
match result {
|
||||
Ok(pair) => {
|
||||
pairs.insert(decision.source, pair);
|
||||
}
|
||||
Err(message) => conflict(
|
||||
plan,
|
||||
Some(decision.source),
|
||||
targets.get(&decision.source).copied(),
|
||||
None,
|
||||
message,
|
||||
),
|
||||
}
|
||||
}
|
||||
pairs
|
||||
}
|
||||
|
||||
/// Adapt only the transferred endpoint value. Reviewed baseline facts continue
|
||||
/// to retain the actual participant/port identities on their respective sides.
|
||||
pub(crate) fn review_endpoint_fact(
|
||||
project: &Project,
|
||||
request: &TransitionRequest,
|
||||
map: &BTreeMap<Id, Id>,
|
||||
source: Id,
|
||||
target: Id,
|
||||
fact: &FacetReview,
|
||||
) -> Result<Option<FacetReview>, String> {
|
||||
let (Some(a), Some(b)) = (
|
||||
project.relations.get(&source),
|
||||
project.relations.get(&target),
|
||||
) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let forward =
|
||||
a.kind() == RelationKind::CommunicationMean && b.kind() == RelationKind::BoundaryExchange;
|
||||
let reverse =
|
||||
a.kind() == RelationKind::BoundaryExchange && b.kind() == RelationKind::CommunicationMean;
|
||||
if !forward && !reverse {
|
||||
return Ok(None);
|
||||
}
|
||||
let (endpoint, is_source) = match fact {
|
||||
FacetReview::Relation(RelationReview::Source(_)) => (a.source(), true),
|
||||
FacetReview::Relation(RelationReview::Target(_)) => (a.target(), false),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
let mapped = if forward {
|
||||
let decision = request.communication_endpoints.iter().find(|d| d.source == source)
|
||||
.ok_or("Taking an OA endpoint requires this exchange's explicit SA component-port decision.")?;
|
||||
let port = port_id(if is_source {
|
||||
&decision.source_port
|
||||
} else {
|
||||
&decision.target_port
|
||||
});
|
||||
let expected_owner = map.get(&endpoint).ok_or(
|
||||
"Review the participant counterpart before taking its communication endpoint.",
|
||||
)?;
|
||||
let e = project
|
||||
.elements
|
||||
.get(&port)
|
||||
.ok_or("Selected communication endpoint port does not exist.")?;
|
||||
if e.parent != Some(*expected_owner)
|
||||
|| e.layer() != Layer::Sa
|
||||
|| !matches!(
|
||||
crate::ports::port_contract(e),
|
||||
Some(PortContract::Component { .. })
|
||||
)
|
||||
{
|
||||
return Err(
|
||||
"Selected component port disagrees with the reviewed participant counterpart."
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
port
|
||||
} else {
|
||||
let owner = project
|
||||
.elements
|
||||
.get(&endpoint)
|
||||
.and_then(|e| e.parent)
|
||||
.ok_or("SA boundary port needs a participant owner.")?;
|
||||
let participant = *map.get(&owner).ok_or(
|
||||
"Review the SA port owner's OA participant counterpart before taking this endpoint.",
|
||||
)?;
|
||||
if !project.elements.get(&participant).is_some_and(|e| {
|
||||
e.layer() == Layer::Oa && matches!(e.kind(), Kind::Entity | Kind::Actor)
|
||||
}) {
|
||||
return Err("Boundary endpoint owner must map to an OA entity or actor.".into());
|
||||
}
|
||||
participant
|
||||
};
|
||||
let mut result = fact.clone();
|
||||
if let FacetReview::Relation(RelationReview::Source(values) | RelationReview::Target(values)) =
|
||||
&mut result
|
||||
{
|
||||
match request.direction.source_layer() {
|
||||
Layer::Oa => values.oa = Observed::Value(mapped),
|
||||
Layer::Sa => values.sa = Observed::Value(mapped),
|
||||
}
|
||||
}
|
||||
Ok(Some(result))
|
||||
}
|
||||
Reference in New Issue
Block a user