Complete source 6 engineering workflows and preserve release validation evidence
This commit is contained in:
@@ -139,6 +139,36 @@ pub fn decode_with_report_limited(
|
||||
}
|
||||
let decoded = if bytes.iter().find(|b| !b.is_ascii_whitespace()) == Some(&b'{') {
|
||||
let header: serde_json::Value = serde_json::from_slice(bytes)?;
|
||||
if header
|
||||
.get("schema_version")
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
.is_some_and(|v| v <= 5)
|
||||
&& let Some(diagrams) = header
|
||||
.get("diagrams")
|
||||
.and_then(serde_json::Value::as_object)
|
||||
{
|
||||
for diagram in diagrams.values().filter_map(serde_json::Value::as_object) {
|
||||
for key in diagram.keys() {
|
||||
if key == "view_options" {
|
||||
return Err(FileError::Invalid("Diagram.view_options requires schema/source 6, including an explicitly supplied default value.".into()));
|
||||
}
|
||||
if !matches!(
|
||||
key.as_str(),
|
||||
"id" | "alias"
|
||||
| "name"
|
||||
| "description"
|
||||
| "layer"
|
||||
| "projection"
|
||||
| "nodes"
|
||||
| "routes"
|
||||
) {
|
||||
return Err(FileError::Invalid(format!(
|
||||
"Unknown diagram field '{key}' in the frozen pre6 contract."
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match header
|
||||
.get("schema_version")
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
@@ -151,13 +181,14 @@ pub fn decode_with_report_limited(
|
||||
Some(2) => migrate_v2_limited(serde_json::from_slice(bytes)?, limits)?,
|
||||
Some(3) => v3::migrate(serde_json::from_slice(bytes)?, limits)?,
|
||||
Some(4) => v4::migrate(serde_json::from_slice(bytes)?, limits)?,
|
||||
Some(5) => DecodeResult {
|
||||
Some(5) => migrate_v5(serde_json::from_slice(bytes)?, limits)?,
|
||||
Some(6) => DecodeResult {
|
||||
project: serde_json::from_slice(bytes)?,
|
||||
migration: None,
|
||||
},
|
||||
_ => {
|
||||
return Err(FileError::Invalid(
|
||||
"Unsupported or missing schema_version; expected 1, 2, 3, 4 or 5.".into(),
|
||||
"Unsupported or missing schema_version; expected 1, 2, 3, 4, 5 or 6.".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -186,6 +217,146 @@ pub fn decode_with_report_limited(
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
/// Versions 4 and 5 share unchanged typed records, but their constructor set
|
||||
/// remains frozen. Apply this before normalizing the advertised version.
|
||||
pub(crate) fn pre6_relation_kind(kind: RelationKind) -> bool {
|
||||
matches!(
|
||||
kind,
|
||||
RelationKind::InterfaceItemAllocation
|
||||
| RelationKind::DataAssociation
|
||||
| RelationKind::Allocation
|
||||
| RelationKind::PortAllocation
|
||||
| RelationKind::Interaction
|
||||
| RelationKind::CapabilityInvolvement
|
||||
| RelationKind::ChainInvolvement
|
||||
| RelationKind::RoleRealization
|
||||
| RelationKind::RoleAssignment
|
||||
| RelationKind::FunctionalExchange
|
||||
| RelationKind::BoundaryExchange
|
||||
| RelationKind::MissionCapability
|
||||
| RelationKind::CapabilityInclude
|
||||
| RelationKind::CapabilityExtend
|
||||
| RelationKind::CapabilityGeneralization
|
||||
| RelationKind::MissionInvolvement
|
||||
| RelationKind::SequenceLink
|
||||
| RelationKind::StateTransition
|
||||
| RelationKind::Availability
|
||||
| RelationKind::ExchangeAllocation
|
||||
| RelationKind::InterfaceProvision
|
||||
| RelationKind::InterfaceRequirement
|
||||
| RelationKind::StateAction
|
||||
)
|
||||
}
|
||||
pub(crate) fn pre6_element_kind(kind: Kind) -> bool {
|
||||
matches!(
|
||||
kind,
|
||||
Kind::Entity
|
||||
| Kind::Actor
|
||||
| Kind::Role
|
||||
| Kind::Activity
|
||||
| Kind::Capability
|
||||
| Kind::OperationalMission
|
||||
| Kind::Process
|
||||
| Kind::System
|
||||
| Kind::SystemActor
|
||||
| Kind::Function
|
||||
| Kind::SystemCapability
|
||||
| Kind::Mission
|
||||
| Kind::FunctionalChain
|
||||
| Kind::ExchangeItem
|
||||
| Kind::DataType
|
||||
| Kind::DataField
|
||||
| Kind::Interface
|
||||
| Kind::Port
|
||||
| Kind::Scenario
|
||||
| Kind::Lifeline
|
||||
| Kind::Occurrence
|
||||
| Kind::StateMachine
|
||||
| Kind::Region
|
||||
| Kind::State
|
||||
| Kind::Mode
|
||||
| Kind::Initial
|
||||
| Kind::Final
|
||||
| Kind::Choice
|
||||
| Kind::Junction
|
||||
| Kind::Fork
|
||||
| Kind::Join
|
||||
| Kind::ShallowHistory
|
||||
| Kind::DeepHistory
|
||||
| Kind::EntryPoint
|
||||
| Kind::ExitPoint
|
||||
| Kind::Terminate
|
||||
| Kind::Event
|
||||
| Kind::Constraint
|
||||
)
|
||||
}
|
||||
pub(crate) fn check_pre6_relations<'a>(
|
||||
relations: impl Iterator<Item = &'a Relation>,
|
||||
) -> Result<(), FileError> {
|
||||
for relation in relations {
|
||||
if !pre6_relation_kind(relation.kind()) {
|
||||
return Err(FileError::Invalid(format!(
|
||||
"{:?} requires schema/source 6.",
|
||||
relation.kind()
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub(crate) fn check_pre6_elements<'a>(
|
||||
elements: impl Iterator<Item = &'a Element>,
|
||||
) -> Result<(), FileError> {
|
||||
for element in elements {
|
||||
if !pre6_element_kind(element.kind()) {
|
||||
return Err(FileError::Invalid(format!(
|
||||
"{:?} requires schema/source 6.",
|
||||
element.kind()
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub(crate) fn check_pre6_baseline(baseline: &SemanticBaseline) -> Result<(), FileError> {
|
||||
if baseline.facets.iter().any(|f| {
|
||||
matches!(
|
||||
f,
|
||||
FacetReview::Category(_)
|
||||
| FacetReview::Member {
|
||||
collection: ReferenceCollection::CategoryExchanges,
|
||||
..
|
||||
}
|
||||
)
|
||||
}) {
|
||||
return Err(FileError::Invalid(
|
||||
"Exchange category semantic facts require schema/source 6.".into(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub(crate) fn migrate_v5(
|
||||
mut project: Project,
|
||||
limits: crate::language::ParseLimits,
|
||||
) -> Result<DecodeResult, FileError> {
|
||||
if project.schema_version != 5 {
|
||||
return Err(FileError::Invalid("Expected schema_version 5.".into()));
|
||||
}
|
||||
check_pre6_relations(project.relations.values())?;
|
||||
check_pre6_elements(project.elements.values())?;
|
||||
for baseline in project
|
||||
.traces
|
||||
.values()
|
||||
.filter_map(|t| t.baseline.as_ref().and_then(|b| b.semantic.as_ref()))
|
||||
{
|
||||
check_pre6_baseline(baseline)?;
|
||||
}
|
||||
limits.check_model(&project).map_err(FileError::Limit)?;
|
||||
project.schema_version = SCHEMA_VERSION;
|
||||
Ok(DecodeResult {
|
||||
project,
|
||||
migration: Some(MigrationReport::data_upgrade(5)),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn migrate_v1_with_report_limited(
|
||||
old: v1::Project,
|
||||
limits: crate::language::ParseLimits,
|
||||
|
||||
Reference in New Issue
Block a user