feat: checkpoint compact ordinary source and lossless default preservation

This commit is contained in:
2026-09-13 04:14:01 -07:00
parent 2a64344b92
commit adffd19bbc
49 changed files with 4021 additions and 639 deletions
+1
View File
@@ -102,3 +102,4 @@ harness/experiments/default-policy-20260913/** -text whitespace=cr-at-eol
harness/experiments/default-policy-20260913/**/*.log -whitespace
harness/experiments/default-fidelity-20260913/** -text whitespace=cr-at-eol
harness/experiments/default-fidelity-20260913/**/*.log -whitespace
harness/experiments/default-production-20260913/** -text whitespace=cr-at-eol
+49 -15
View File
@@ -3,6 +3,8 @@
mod semantic_source;
#[path = "migration_v2/source.rs"]
mod v2_source;
pub(crate) use crate::source_defaults::Mode as DeclarationFormat;
use crate::source_defaults::{self, Context as DefaultContext};
use crate::*;
use serde_json::{Map, Value};
use std::{collections::BTreeMap, fmt};
@@ -645,25 +647,34 @@ fn offset_for(source: &str, line: usize, column: usize) -> usize {
source.len()
}
/// Canonical output uses persistent aliases and explicit identities. Typed
/// nested records are ordinary language fields; JSON documents are not embedded.
/// Canonical output uses persistent aliases and explicit identities, omitting
/// only values supplied by source defaults. Typed nested records are ordinary
/// language fields; JSON documents are not embedded.
pub fn format(project: &Project) -> String {
let mut out = std::format!(
"arcadia {SCHEMA_VERSION};\nproject {} {} id \"{}\" {{\n description {};\n\n",
"arcadia {SCHEMA_VERSION};\nproject {} {} id \"{}\" {{\n",
project.alias,
quote(&project.name),
project.id,
quote(&project.description)
);
for (_, declaration) in format_declarations(project) {
if !project.description.is_empty() {
out.push_str(&std::format!(
" description {};\n",
quote(&project.description)
));
}
out.push('\n');
for (_, declaration) in format_declarations(project, DeclarationFormat::Compact) {
out.push_str(&declaration);
}
out.push_str("}\n");
out
}
/// The same typed declaration writer is used by flat and modular source output.
pub(crate) fn format_declarations(project: &Project) -> Vec<(Id, String)> {
/// Shared typed writer for canonical output and lossless editing. Complete edit
/// comparisons retain defaults; emitted declarations and value fragments apply
/// only the omission rules valid in their selected source context.
pub(crate) fn format_declarations(project: &Project, mode: DeclarationFormat) -> Vec<(Id, String)> {
let aliases = project
.identities()
.into_iter()
@@ -675,6 +686,7 @@ pub(crate) fn format_declarations(project: &Project) -> Vec<(Id, String)> {
let mut fields = payload(&serde_json::to_value(&e.body).unwrap());
fields.insert("description".into(), Value::String(e.description.clone()));
fields.insert("parent".into(), serde_json::to_value(e.parent).unwrap());
let fields = source_defaults::fields(DefaultContext::Element(e.kind()), &fields, mode);
write_declaration(
&mut out,
&element_word(e.kind(), e.layer()),
@@ -690,6 +702,7 @@ pub(crate) fn format_declarations(project: &Project) -> Vec<(Id, String)> {
let mut out = String::new();
let mut fields = payload(&serde_json::to_value(&r.body).unwrap());
fields.insert("description".into(), Value::String(r.description.clone()));
let fields = source_defaults::fields(DefaultContext::Relation(r.kind()), &fields, mode);
write_declaration(
&mut out,
&relation_word(r.kind()),
@@ -705,6 +718,7 @@ pub(crate) fn format_declarations(project: &Project) -> Vec<(Id, String)> {
let mut out = String::new();
let mut fields = record_fields(d);
fields.remove("nodes");
let fields = source_defaults::fields(DefaultContext::Diagram, &fields, mode);
write_declaration(
&mut out,
"diagram",
@@ -719,6 +733,7 @@ pub(crate) fn format_declarations(project: &Project) -> Vec<(Id, String)> {
let mut out = String::new();
let mut fields = record_fields(n);
fields.insert("diagram".into(), Value::String(d.id.to_string()));
let fields = source_defaults::fields(DefaultContext::Node, &fields, mode);
write_declaration(&mut out, "node", &n.alias, None, n.id, &fields, &aliases);
declarations.push((n.id, out));
}
@@ -731,7 +746,7 @@ pub(crate) fn format_declarations(project: &Project) -> Vec<(Id, String)> {
&a.alias,
Some(&a.name),
a.id,
&record_fields(a),
&source_defaults::fields(DefaultContext::Artifact, &record_fields(a), mode),
&aliases,
);
declarations.push((a.id, out));
@@ -744,7 +759,7 @@ pub(crate) fn format_declarations(project: &Project) -> Vec<(Id, String)> {
&t.alias,
Some(&t.name),
t.id,
&record_fields(t),
&source_defaults::fields(DefaultContext::Trace, &record_fields(t), mode),
&aliases,
);
declarations.push((t.id, out));
@@ -1742,7 +1757,9 @@ pub(crate) fn resolve_declarations(
*viewpoint = ScenarioViewpoint::MixedLegacy;
}
let mut body = serde_json::to_value(initial).unwrap();
let defaults = if legacy {
let defaults = if project.schema_version >= 7 {
source_defaults::parser_fields(DefaultContext::Element(kind))
} else if legacy {
crate::persistence::v3::legacy_defaults(kind)
.and_then(|value| value.as_object().cloned())
.unwrap_or_else(|| payload(&body))
@@ -1799,7 +1816,12 @@ pub(crate) fn resolve_declarations(
let mut body =
serde_json::to_value(RelationBody::new(kind, Id::nil(), Id::nil()))
.unwrap();
let merged = merge_fields(payload(&body), fields).map_err(&conversion_error)?;
let defaults = if project.schema_version >= 7 {
source_defaults::parser_fields(DefaultContext::Relation(kind))
} else {
payload(&body)
};
let merged = merge_fields(defaults, fields).map_err(&conversion_error)?;
replace_payload(&mut body, merged);
let body = serde_json::from_value(body)
.map_err(|e| conversion_error(e.to_string()))?;
@@ -1824,7 +1846,10 @@ pub(crate) fn resolve_declarations(
}
let defaults =
serde_json::json!({"description":"","layer":"Oa","projection":"Overview","routes":[],"view_options":crate::view_options::ViewOptions::default()});
let mut record = merge_fields(defaults.as_object().unwrap().clone(), fields)
let defaults = if project.schema_version >= 7 {
source_defaults::parser_fields(DefaultContext::Diagram)
} else { defaults.as_object().unwrap().clone() };
let mut record = merge_fields(defaults, fields)
.map_err(&conversion_error)?;
header(&mut record, d, true);
record.insert("nodes".into(), serde_json::json!({}));
@@ -1839,7 +1864,10 @@ pub(crate) fn resolve_declarations(
return Err(conversion_error("Missing required field 'element'.".into()));
}
let defaults = serde_json::json!({"element":null,"position":{"x":0.0,"y":0.0},"size":{"width":224.0,"height":94.0},"collapsed":false,"port_anchors":[]});
let mut record = merge_fields(defaults.as_object().unwrap().clone(), fields)
let defaults = if project.schema_version >= 7 {
source_defaults::parser_fields(DefaultContext::Node)
} else { defaults.as_object().unwrap().clone() };
let mut record = merge_fields(defaults, fields)
.map_err(&conversion_error)?;
header(&mut record, d, false);
let node: NodePlacement = serde_json::from_value(Value::Object(record))
@@ -1851,7 +1879,10 @@ pub(crate) fn resolve_declarations(
return Err(conversion_error("Missing required field 'owner'.".into()));
}
let defaults = serde_json::json!({"description":"","owner":null,"relation":"References","uri":"","revision":null,"fragment":null});
let mut record = merge_fields(defaults.as_object().unwrap().clone(), fields)
let defaults = if project.schema_version >= 7 {
source_defaults::parser_fields(DefaultContext::Artifact)
} else { defaults.as_object().unwrap().clone() };
let mut record = merge_fields(defaults, fields)
.map_err(&conversion_error)?;
header(&mut record, d, true);
let artifact = serde_json::from_value(Value::Object(record))
@@ -1867,7 +1898,10 @@ pub(crate) fn resolve_declarations(
}
}
let defaults = serde_json::json!({"description":"","oa":null,"sa":null,"kind":"Refinement","baseline":null});
let mut record = merge_fields(defaults.as_object().unwrap().clone(), fields)
let defaults = if project.schema_version >= 7 {
source_defaults::parser_fields(DefaultContext::Trace)
} else { defaults.as_object().unwrap().clone() };
let mut record = merge_fields(defaults, fields)
.map_err(&conversion_error)?;
header(&mut record, d, true);
let trace = if project.schema_version < 5 {
+1
View File
@@ -10,6 +10,7 @@ pub mod references;
pub mod routing;
pub mod semantic;
pub mod session;
mod source_defaults;
pub mod source_files;
pub mod source_project;
pub mod topology_context;
+382
View File
@@ -0,0 +1,382 @@
//! Ordinary source defaults shared by current parsing and declaration rendering.
//!
//! These are source omission defaults, not graphical creation defaults. The
//! policy operates on complete typed declaration maps. Required named fields
//! and selected constructor payloads remain distinct from omission defaults.
//! Historical versions use their existing frozen parser paths. Semantic baseline
//! history is never traversed or normalized here.
use crate::{Kind, RelationKind};
use serde_json::{Map, Value, json};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Context {
Element(Kind),
Relation(RelationKind),
Diagram,
Node,
Artifact,
Trace,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Mode {
CompleteForEdit,
Compact,
/// Internal lossless-edit fragments: retain every top-level value while
/// compacting its selected constructor's optional nested fields.
CompactValues,
}
/// Top-level field inventory for the current ordinary reader. Callers must
/// check required fields before merging; null entries only admit their names.
/// This never supplies defaults inside a selected constructor.
pub(crate) fn parser_fields(context: Context) -> Map<String, Value> {
let mut fields = declaration_defaults(context);
let required: &[&str] = match context {
Context::Element(kind) => {
fields.remove("description");
fields.remove("parent");
if kind == Kind::Port {
&["contract"]
} else {
&[]
}
}
Context::Relation(kind) => {
fields.remove("description");
match kind {
RelationKind::ContextualLink => &["owner", "source", "target", "purpose"],
RelationKind::FunctionalExchange => {
&["source", "target", "source_port", "target_port"]
}
_ => &["source", "target"],
}
}
Context::Diagram => &[],
Context::Node => &["element"],
Context::Artifact => &["owner"],
Context::Trace => &["oa", "sa"],
};
for name in required {
fields.insert((*name).into(), Value::Null);
}
fields
}
pub(crate) fn fields(
context: Context,
input: &Map<String, Value>,
mode: Mode,
) -> Map<String, Value> {
let mut result = input.clone();
restore_serialization_skips(context, &mut result);
if mode != Mode::CompleteForEdit {
compact_nested(context, &mut result);
}
if mode == Mode::Compact {
// Compact a composite and its declared default by the same nested rules
// before whole-field comparison. This also makes compact input stable:
// e.g. String{minimum_length:0} still denotes the default definition.
let mut defaults = declaration_defaults(context);
compact_nested(context, &mut defaults);
omit_defaults(&mut result, &defaults);
}
result
}
fn restore_serialization_skips(context: Context, fields: &mut Map<String, Value>) {
match context {
Context::Element(Kind::Occurrence) => {
if let Some(exchange) = constructor_payload(fields, "body", "Exchange") {
exchange
.entry("operation".to_owned())
.or_insert(Value::Null);
}
}
Context::Node => {
fields.entry("port_anchors".to_owned()).or_insert(json!([]));
}
Context::Trace => {
if let Some(baseline) = fields.get_mut("baseline").and_then(Value::as_object_mut) {
baseline.entry("semantic".to_owned()).or_insert(Value::Null);
}
}
_ => {}
}
}
fn declaration_defaults(context: Context) -> Map<String, Value> {
let mut defaults = match context {
Context::Element(kind) => element_defaults(kind),
Context::Relation(kind) => relation_defaults(kind),
Context::Diagram => object(json!({
"description": "", "layer": "Oa", "projection": "Overview",
"routes": [], "view_options": {
"hidden_edges": [], "relation_kinds": "All",
"work_categories": "All", "participant_categories": "All",
"coverage": "All", "work_contexts": "All", "exchange_labels": "Name"
}
})),
Context::Node => object(json!({
"position": {"x": 0.0, "y": 0.0},
"size": {"width": 224.0, "height": 94.0},
"collapsed": false, "port_anchors": []
})),
Context::Artifact => object(json!({
"description": "", "relation": "References", "uri": "",
"revision": null, "fragment": null
})),
Context::Trace => object(json!({
"description": "", "kind": "Refinement", "baseline": null
})),
};
match context {
Context::Element(_) => {
defaults.insert("description".into(), json!(""));
defaults.insert("parent".into(), Value::Null);
}
Context::Relation(_) => {
defaults.insert("description".into(), json!(""));
}
_ => {}
}
defaults
}
fn element_defaults(kind: Kind) -> Map<String, Value> {
if let Some(state_kind) = kind.state_kind() {
// The declaration selects this discriminator; no GUI factory is used.
return object(json!({
"kind": state_kind, "entry": "", "exit": "", "do_activity": ""
}));
}
match kind {
Kind::Process | Kind::FunctionalChain => object(json!({"steps": []})),
Kind::ExchangeItem => object(json!({
"kind": "Information", "mechanism": "Unspecified", "supertype": null
})),
Kind::ExchangeCategory => object(json!({"family": "Work", "exchanges": []})),
Kind::DataType => object(json!({
"definition": {"String": {"minimum_length": 0, "maximum_length": null}},
"default": null, "unit": ""
})),
Kind::DataField => object(json!({
"type_ref": "String", "minimum": 1, "maximum": 1, "unit": "",
"direction": "Unspecified", "storage": "Value", "default": null
})),
Kind::Interface => object(json!({"supertype": null})),
// Port.contract is required, even when its explicit value is Legacy.
Kind::Port => object(json!({"legacy": null})),
Kind::Scenario => object(json!({
"viewpoint": "MixedLegacy", "context": null,
"precondition": "", "postcondition": "", "steps": []
})),
Kind::Lifeline => object(json!({"represented": null})),
Kind::Occurrence => object(json!({
"body": {"Exchange": {
"exchange": null, "sender": null, "receiver": null,
"message_kind": "Asynchronous", "reply_to": null,
"values": [], "operation": null
}},
"precondition": "", "postcondition": ""
})),
Kind::Event => object(json!({"body": {"Signal": {"items": []}}})),
Kind::Constraint => object(json!({"expression": "", "constrained": []})),
// Unit-body declarations have no additional omission defaults. A future
// Kind reaches this branch without gaining speculative payload rules.
_ => Map::new(),
}
}
fn relation_defaults(kind: RelationKind) -> Map<String, Value> {
match kind {
RelationKind::InterfaceItemAllocation => object(json!({
"order": 0, "transmission": "Unspecified", "acquisition": "Unspecified"
})),
RelationKind::DataAssociation => object(json!({
"source_role": "", "target_role": "",
"source_minimum": 0, "target_minimum": 0,
"source_maximum": null, "target_maximum": null,
"source_navigable": false, "target_navigable": false
})),
RelationKind::Interaction | RelationKind::FunctionalExchange => {
object(json!({"items": []}))
}
RelationKind::BoundaryExchange => object(json!({"items": [], "oriented": true})),
RelationKind::CommunicationMean => object(json!({"items": [], "oriented": false})),
RelationKind::CapabilityExtend => object(json!({"condition": ""})),
RelationKind::SequenceLink => object(json!({"guard": ""})),
RelationKind::StateTransition => object(json!({
"transition_kind": "External", "trigger": "", "guard": "", "effect": "",
"triggers": [], "effects": []
})),
RelationKind::StateAction => object(json!({"phase": "Do"})),
// source/target, FunctionalExchange ports and ContextualLink's owner,
// endpoints and purpose are required, despite factory placeholder values.
_ => Map::new(),
}
}
fn compact_nested(context: Context, fields: &mut Map<String, Value>) {
match context {
Context::Element(Kind::DataType) => compact_data_definition(fields),
Context::Element(Kind::Port) => {
if let Some(legacy) = fields.get_mut("legacy").and_then(Value::as_object_mut) {
omit_nulls(legacy, &["interface"]);
}
// Every selected PortContract payload field remains required.
}
Context::Element(Kind::Occurrence) => compact_occurrence(fields),
Context::Diagram => {
if let Some(options) = fields
.get_mut("view_options")
.and_then(Value::as_object_mut)
{
omit_defaults(
options,
&object(json!({
"hidden_edges": [], "relation_kinds": "All",
"work_categories": "All", "participant_categories": "All",
"coverage": "All", "work_contexts": "All", "exchange_labels": "Name"
})),
);
}
if let Some(routes) = fields.get_mut("routes").and_then(Value::as_array_mut) {
for route in routes {
if let Some(route) = route.as_object_mut() {
omit_defaults(route, &object(json!({"waypoints": [], "label": null})));
}
}
}
// Route entries, EdgeLabel coordinates, projection machine IDs, and
// Only([])/Any([]) selectors remain even when their values are empty.
}
Context::Trace => {
if let Some(baseline) = fields.get_mut("baseline").and_then(Value::as_object_mut) {
omit_nulls(baseline, &["semantic"]);
}
// No recursion through snapshots or semantic historical facets.
}
// DataValue/TypeRef payloads, EventBody fields, Node geometry/anchors,
// contextual endpoints/purposes, and other required records stay intact.
_ => {}
}
}
fn compact_data_definition(fields: &mut Map<String, Value>) {
for tag in ["Integer", "Real"] {
if let Some(payload) = constructor_payload(fields, "definition", tag) {
omit_nulls(payload, &["minimum", "maximum"]);
}
}
if let Some(payload) = constructor_payload(fields, "definition", "String") {
omit_nulls(payload, &["maximum_length"]);
// minimum_length is required inside a selected String constructor.
}
if let Some(payload) = constructor_payload(fields, "definition", "Record") {
omit_nulls(payload, &["supertype"]);
}
// Boolean has no payload; Enumeration.literals remains required even [].
}
fn compact_occurrence(fields: &mut Map<String, Value>) {
if let Some(exchange) = constructor_payload(fields, "body", "Exchange") {
omit_defaults(
exchange,
&object(json!({
"exchange": null, "sender": null, "receiver": null,
"message_kind": "Asynchronous", "reply_to": null,
"values": [], "operation": null
})),
);
if let Some(operation) = exchange.get_mut("operation").and_then(Value::as_object_mut) {
omit_nulls(operation, &["allocation"]);
// Keep the containing operation field even if its record becomes {}:
// explicit unresolved operation intent is not an absent operation.
}
if let Some(values) = exchange.get_mut("values").and_then(Value::as_array_mut) {
for value in values {
if let Some(message_value) = value.as_object_mut() {
omit_defaults(message_value, &object(json!({"path": []})));
}
}
}
// MessageKind::Timer.expression, MessageValue.field/expression remain.
}
for (tag, optional) in [
("Action", &["action", "lifeline"][..]),
("State", &["state", "lifeline"][..]),
("SequenceReference", &["sequence"][..]),
("ScenarioUse", &["scenario"][..]),
] {
if let Some(payload) = constructor_payload(fields, "body", tag) {
omit_nulls(payload, optional);
}
}
// ScenarioUse.bindings, Control.kind, Fragment.operator/branches and
// Branch.guard/occurrences are required, including empty strings/lists.
}
fn constructor_payload<'a>(
fields: &'a mut Map<String, Value>,
key: &str,
tag: &str,
) -> Option<&'a mut Map<String, Value>> {
let wrapper = fields.get_mut(key)?.as_object_mut()?;
if wrapper.len() != 1 {
return None;
}
wrapper.get_mut(tag)?.as_object_mut()
}
fn omit_nulls(fields: &mut Map<String, Value>, names: &[&str]) {
for name in names {
if fields.get(*name).is_some_and(Value::is_null) {
fields.remove(*name);
}
}
}
fn omit_defaults(fields: &mut Map<String, Value>, defaults: &Map<String, Value>) {
for (name, default) in defaults {
if fields.get(name).is_some_and(|value| same(value, default)) {
fields.remove(name);
}
}
}
// Comparison recurses only to compare a known field with its exact declared
// default. It never traverses arbitrary data looking for values to remove.
fn same(value: &Value, default: &Value) -> bool {
match (value, default) {
(Value::Object(a), Value::Object(b)) => {
a.len() == b.len()
&& a.iter()
.all(|(key, value)| b.get(key).is_some_and(|d| same(value, d)))
}
(Value::Array(a), Value::Array(b)) => {
a.len() == b.len() && a.iter().zip(b).all(|(value, d)| same(value, d))
}
(Value::Number(a), Value::Number(b)) => {
// serde_json numeric equality alone can equate -0.0 with +0.0.
// Extracted f32 coordinates are represented exactly as f64 here.
a == b
&& match (a.as_f64(), b.as_f64()) {
(Some(x), Some(y)) if x == 0.0 && y == 0.0 => x.to_bits() == y.to_bits(),
_ => true,
}
}
_ => value == default,
}
}
fn object(value: Value) -> Map<String, Value> {
match value {
Value::Object(fields) => fields,
_ => unreachable!("literal source default is an object"),
}
}
#[cfg(test)]
#[path = "source_defaults_tests.rs"]
mod tests;
@@ -1,12 +1,158 @@
//! Independent field-map oracles for an isolated source-7 policy experiment.
//! This does not wire the policy into the production formatter or source editor.
#[path = "../../../harness/prototypes/source-defaults-20260913.rs"]
mod policy;
use oa_core::{Kind, RelationKind};
use policy::{Context, Mode};
//! Independent literal oracles for the production source-default policy.
use super::{self as policy, Context, Mode};
use crate::{Kind, RelationKind};
use serde_json::{Value, json};
#[test]
fn complete_comparison_keeps_populated_serializer_skip_values() {
for (context, input) in [
(
Context::Element(Kind::Occurrence),
json!({"body":{"Exchange":{"operation":{"allocation":"allocation-id"},"values":[]}}}),
),
(
Context::Node,
json!({"port_anchors":[{"port":"port-id","side":"Left","offset":0.0}]}),
),
(
Context::Trace,
json!({"baseline":{"semantic":{"historical":"payload"}}}),
),
] {
assert_eq!(rendered(context, &input, Mode::CompleteForEdit), input);
}
}
#[test]
fn compact_value_fragments_keep_selected_constructor_requirements() {
for (context, input, expected) in [
(
Context::Element(Kind::DataType),
json!({"definition":{"String":{"minimum_length":0,"maximum_length":null}},"default":null,"unit":""}),
json!({"definition":{"String":{"minimum_length":0}},"default":null,"unit":""}),
),
(
Context::Element(Kind::Event),
json!({"body":{"Signal":{"items":[]}}}),
json!({"body":{"Signal":{"items":[]}}}),
),
(
Context::Element(Kind::Occurrence),
json!({"body":{"Exchange":{"operation":{"allocation":null},"values":[]}},"precondition":""}),
json!({"body":{"Exchange":{"operation":{}}},"precondition":""}),
),
(
Context::Element(Kind::Occurrence),
json!({"body":{"ScenarioUse":{"scenario":null,"bindings":[]}}}),
json!({"body":{"ScenarioUse":{"bindings":[]}}}),
),
] {
assert_eq!(rendered(context, &input, Mode::CompactValues), expected);
}
}
#[test]
fn source_scenario_default_is_independent_of_graphical_creation() {
use crate::{ElementBody, Layer, ScenarioViewpoint, SharedBody, language};
let project = language::parse(
"arcadia 7; project p \"P\" { oa_scenario o \"O\" {} sa_scenario s \"S\" {} }",
)
.unwrap();
for element in project.elements.values() {
assert!(matches!(
element.body.shared(),
Some(SharedBody::Scenario {
viewpoint: ScenarioViewpoint::MixedLegacy,
..
})
));
assert!(!matches!(
ElementBody::new(Kind::Scenario, element.layer())
.unwrap()
.shared(),
Some(SharedBody::Scenario {
viewpoint: ScenarioViewpoint::MixedLegacy,
..
})
));
}
let source = language::format(&project);
assert!(!source.contains("description"));
assert!(!source.contains("parent"));
assert!(!source.contains("viewpoint"));
assert_eq!(language::parse(&source).unwrap(), project);
let mut typed = project;
for element in typed.elements.values_mut() {
let viewpoint = if element.layer() == Layer::Oa {
ScenarioViewpoint::OperationalActivity
} else {
ScenarioViewpoint::Functional
};
*element.body.shared_mut().unwrap() = SharedBody::Scenario {
viewpoint,
context: None,
precondition: String::new(),
postcondition: String::new(),
steps: vec![],
};
}
let source = language::format(&typed);
assert!(source.contains("viewpoint OperationalActivity;"));
assert!(source.contains("viewpoint Functional;"));
assert_eq!(language::parse(&source).unwrap(), typed);
}
#[test]
fn shared_parser_defaults_preserve_required_fields_and_kind_checks() {
for (declaration, message) in [
(
"oa_port p0 \"Port\" {}",
"Missing required field 'contract'",
),
(
"allocation r \"R\" { source @owner; }",
"Missing required field 'target'",
),
(
"functional_exchange r \"R\" { source @owner; target @work; }",
"Missing required field 'source_port'",
),
(
"contextual_link r \"R\" {}",
"Missing required field 'owner'",
),
("node n { diagram @d; }", "Missing required field 'element'"),
("artifact a \"A\" {}", "Missing required field 'owner'"),
("trace t \"T\" {}", "Missing required field 'oa'"),
(
"oa_state s \"S\" { kind Initial; }",
"Declaration kind disagrees",
),
(
"oa_activity a \"A\" { unknown true; }",
"Unknown field 'unknown'",
),
(
"oa_data_type t \"T\" { definition { String {}; }; }",
"minimum_length",
),
(
"oa_occurrence o \"O\" { body { ScenarioUse {}; }; }",
"bindings",
),
] {
let source = format!(
"arcadia 7; project p \"P\" {{ oa_entity owner \"Owner\" {{}} oa_activity work \"Work\" {{}} diagram d \"D\" {{}} {declaration} }}"
);
let errors = crate::language::parse(&source).unwrap_err();
assert!(
errors.iter().any(|error| error.message.contains(message)),
"{declaration}: {errors:?}"
);
}
}
fn rendered(context: Context, input: &Value, mode: Mode) -> Value {
Value::Object(policy::fields(context, input.as_object().unwrap(), mode))
}
+131 -20
View File
@@ -726,7 +726,8 @@ fn apply_edits(bundle: &mut SourceBundle, edits: Edits) -> Result<(), Vec<Source
pub fn format_bundle(project: &Project) -> SourceBundle {
let ownership = default_ownership(project);
let aliases: BTreeMap<_, _> = project.identities().into_iter().collect();
let mut declarations = language::format_declarations(project);
let mut declarations =
language::format_declarations(project, language::DeclarationFormat::Compact);
declarations.sort_by_key(|(id, _)| declaration_order(project, *id, &aliases));
let mut files: BTreeMap<LogicalPath, String> = BTreeMap::new();
for (id, declaration) in declarations {
@@ -735,11 +736,13 @@ pub fn format_bundle(project: &Project) -> SourceBundle {
.or_default()
.push_str(&unindent(&declaration));
}
let mut entry = format!(
"arcadia {SCHEMA_VERSION};\n{}{{\n description {};\n",
project_header(project),
quote(&project.description)
);
let mut entry = format!("arcadia {SCHEMA_VERSION};\n{}{{\n", project_header(project));
if !project.description.is_empty() {
entry.push_str(&format!(
" description {};\n",
quote(&project.description)
));
}
for path in files.keys() {
entry.push_str(&format!(" include {};\n", quote(path)));
}
@@ -850,8 +853,25 @@ pub fn update_bundle_with_identity_map(
)]);
}
let origins: BTreeMap<_, _> = identities.iter().map(|(old, new)| (*new, *old)).collect();
let before: BTreeMap<_, _> = language::format_declarations(old).into_iter().collect();
let after: BTreeMap<_, _> = language::format_declarations(project).into_iter().collect();
// Comparison must include defaults even when persistence serialization or
// compact output omits them. Otherwise an unrelated payload edit erases
// unchanged explicit fields from the authored document.
let before: BTreeMap<_, _> =
language::format_declarations(old, language::DeclarationFormat::CompleteForEdit)
.into_iter()
.collect();
let after: BTreeMap<_, _> =
language::format_declarations(project, language::DeclarationFormat::CompleteForEdit)
.into_iter()
.collect();
let compact: BTreeMap<_, _> =
language::format_declarations(project, language::DeclarationFormat::Compact)
.into_iter()
.collect();
let compact_values: BTreeMap<_, _> =
language::format_declarations(project, language::DeclarationFormat::CompactValues)
.into_iter()
.collect();
let old_aliases: BTreeMap<_, _> = old
.identities()
.into_iter()
@@ -907,6 +927,27 @@ pub fn update_bundle_with_identity_map(
};
let mut previous = parse(old_text)?;
let mut next = parse(new_text)?;
let next_id = identities[&id];
let value_text = &compact_values[&next_id];
let insertion = InsertionSyntax::new(
&next.fields,
&parse(value_text)?.fields,
value_text,
&parse(&compact[&next_id])?.fields,
);
let old_kind = previous.header["kind"];
let new_kind = next.header["kind"];
if old_text[old_kind.start..old_kind.end] != new_text[new_kind.start..new_kind.end]
&& let language::EditKind::Record(actual_fields) = &actual.fields.kind
&& let language::EditKind::Record(previous_fields) = &mut previous.fields.kind
{
// An implicit value is supplied by its declaration context.
// After a kind change, an unchanged value may need explicit
// syntax (e.g. BoundaryExchange true -> CommunicationMean true).
// Reconsider only absent fields; authored explicit values still
// compare against their complete old values and keep trivia.
previous_fields.retain(|key, _| actual_fields.contains_key(key));
}
if actual.lexical_owner.is_some()
&& let language::EditKind::Record(fields) = &actual.fields.kind
&& !fields.contains_key("parent")
@@ -923,6 +964,7 @@ pub fn update_bundle_with_identity_map(
source,
before: old_text,
after: new_text,
insertion: Some(&insertion),
old_aliases: &old_aliases,
new_aliases: &new_aliases,
legacy_value_paths: false,
@@ -953,6 +995,7 @@ pub fn update_bundle_with_identity_map(
source,
before: &before,
after: &after,
insertion: None,
old_aliases: &old_aliases,
new_aliases: &new_aliases,
legacy_value_paths: false,
@@ -1011,7 +1054,7 @@ pub fn update_bundle_with_identity_map(
project,
identities,
&original_order,
&after,
&compact,
limits,
)?;
let result = compile_bundle(&bundle, limits)?;
@@ -1119,10 +1162,77 @@ fn inserted_value(text: &str, indent: &str, source: &str) -> String {
output
}
// Value fragments retain required payload fields even when the enclosing
// declaration field is itself omittable. For example, a selected String type
// needs minimum_length; an Event Signal needs items. The complete tree remains
// the comparison authority; this parallel tree is only an insertion template.
struct InsertionSyntax {
values: BTreeMap<usize, String>,
omitted_fields: BTreeSet<usize>,
}
impl InsertionSyntax {
fn new(
complete: &language::EditValue,
compact_values: &language::EditValue,
source: &str,
compact: &language::EditValue,
) -> Self {
let mut result = Self {
values: BTreeMap::new(),
omitted_fields: BTreeSet::new(),
};
result.collect(complete, compact_values, source);
if let (language::EditKind::Record(all), language::EditKind::Record(kept)) =
(&complete.kind, &compact.kind)
{
for (key, field) in all {
if !kept.contains_key(key) {
result.omitted_fields.insert(field.value.span.start);
}
}
}
result
}
fn collect(
&mut self,
complete: &language::EditValue,
compact: &language::EditValue,
source: &str,
) {
self.values.insert(
complete.span.start,
source[compact.span.start..compact.span.end].to_owned(),
);
match (&complete.kind, &compact.kind) {
(language::EditKind::Record(all), language::EditKind::Record(kept)) => {
for (key, field) in all {
if let Some(next) = kept.get(key) {
self.collect(&field.value, &next.value, source);
} else {
self.omitted_fields.insert(field.value.span.start);
}
}
}
(language::EditKind::List(all), language::EditKind::List(kept)) => {
assert_eq!(
all.len(),
kept.len(),
"compaction preserves array membership"
);
for (field, next) in all.iter().zip(kept) {
self.collect(field, next, source);
}
}
_ => {}
}
}
}
struct SyntaxUpdate<'a> {
source: &'a str,
before: &'a str,
after: &'a str,
insertion: Option<&'a InsertionSyntax>,
old_aliases: &'a BTreeMap<String, String>,
new_aliases: &'a BTreeMap<String, String>,
// Source3 migration only adds path qualification; the existing field identity
@@ -1131,6 +1241,12 @@ struct SyntaxUpdate<'a> {
identity_origins: Option<&'a BTreeMap<Id, Id>>,
}
impl SyntaxUpdate<'_> {
fn inserted(&self, value: &language::EditValue) -> &str {
self.insertion
.and_then(|syntax| syntax.values.get(&value.span.start))
.map(String::as_str)
.unwrap_or(&self.after[value.span.start..value.span.end])
}
fn header(
&self,
actual: &BTreeMap<&str, Span>,
@@ -1243,7 +1359,7 @@ impl SyntaxUpdate<'_> {
_ => {
erase_syntax(self.source, actual.span, edits);
let text = inserted_value(
&self.after[after.span.start..after.span.end],
self.inserted(after),
indent_at(self.source, actual.span.start),
self.source,
);
@@ -1308,6 +1424,9 @@ impl SyntaxUpdate<'_> {
.iter()
.filter(|(key, field)| {
!actual.contains_key(*key)
&& self.insertion.is_none_or(|syntax| {
!syntax.omitted_fields.contains(&field.value.span.start)
})
&& before
.and_then(|fields| fields.get(*key))
.is_none_or(|old| !self.same(&old.value, &field.value))
@@ -1326,11 +1445,7 @@ impl SyntaxUpdate<'_> {
.unwrap_or_else(|| format!("{} ", indent_at(self.source, span.start)));
let mut text = String::new();
for (key, field) in added {
let value = inserted_value(
&self.after[field.value.span.start..field.value.span.end],
&indent,
self.source,
);
let value = inserted_value(self.inserted(&field.value), &indent, self.source);
if multiline {
text.push_str(&indent);
text.push_str(&format!("{key} {value};{}", newline(self.source)));
@@ -1450,11 +1565,7 @@ impl SyntaxUpdate<'_> {
} else {
text.push(' ');
}
text.push_str(&inserted_value(
&self.after[next.span.start..next.span.end],
&indent,
self.source,
));
text.push_str(&inserted_value(self.inserted(next), &indent, self.source));
suffix = "";
}
if i + 1 < after.len() {
@@ -11,7 +11,10 @@ pub(super) fn upgrade(
.as_ref()
.expect("an explicit source migration report");
let project = &current.decoded.project;
let canonical: BTreeMap<_, _> = language::format_declarations(project).into_iter().collect();
let canonical: BTreeMap<_, _> =
language::format_declarations(project, language::DeclarationFormat::CompleteForEdit)
.into_iter()
.collect();
let changed: BTreeSet<_> = report
.semantic_defaults
.iter()
@@ -59,6 +62,7 @@ pub(super) fn upgrade(
source,
before: source,
after,
insertion: None,
old_aliases: &aliases,
new_aliases: &aliases,
legacy_value_paths: true,
+104 -43
View File
@@ -1,8 +1,7 @@
//! Full-model fidelity of the isolated field-deletion adapter over frozen input.
//! This does not install a formatter, exercise lossless model edits, or claim an
//! exhaustive constructor corpus. Legacy fixture migration uses the public parser.
#[path = "../../../harness/prototypes/source-defaults-render-probe-20260913.rs"]
mod render_probe;
//! Public flat/modular formatter fidelity over frozen input and bounded mutations.
//! Legacy fixture migration uses the public parser. The original adapter tests
//! and exact inputs remain archived at source commit 2a64344; this suite exercises
//! production formatters, not that frozen experiment or lossless model edits.
use oa_core::{language, source_project::*, *};
use std::collections::BTreeMap;
@@ -31,14 +30,46 @@ fn identity(project: &Project, alias: &str) -> Id {
.0
}
fn assert_fidelity(label: &str, project: &Project) -> (Project, render_probe::Probe) {
let probe = render_probe::compact(project);
let canonical_model = read(&probe.canonical);
assert_eq!(
canonical_model, *project,
"{label}: original canonical baseline"
);
let compact_model = read(&probe.compact);
fn assert_common_omissions(project: &Project, compiled: &CompiledSourceProject) {
if project.description.is_empty() {
assert!(compiled.index.project_description.is_none());
}
let descriptions = project
.elements
.values()
.map(|e| (e.id, &e.description))
.chain(project.relations.values().map(|r| (r.id, &r.description)))
.chain(project.diagrams.values().map(|d| (d.id, &d.description)))
.chain(
project
.artifact_links
.values()
.map(|a| (a.id, &a.description)),
)
.chain(project.traces.values().map(|t| (t.id, &t.description)));
for (id, description) in descriptions {
if description.is_empty() {
assert!(
!compiled
.index
.fields
.contains_key(&(id, "description".into()))
);
}
}
for element in project.elements.values().filter(|e| e.parent.is_none()) {
assert!(
!compiled
.index
.fields
.contains_key(&(element.id, "parent".into()))
);
}
}
fn assert_fidelity(label: &str, project: &Project) -> (Project, String) {
let flat = language::format(project);
let compact_model = read(&flat);
assert_eq!(
compact_model, *project,
"{label}: every persistent field survives"
@@ -46,7 +77,7 @@ fn assert_fidelity(label: &str, project: &Project) -> (Project, render_probe::Pr
let input = SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([("project.arcadia".into(), probe.compact.clone())]),
files: BTreeMap::from([("project.arcadia".into(), flat.clone())]),
};
let compiled = compile_bundle(&input, ProjectLimits::default()).unwrap();
assert_eq!(
@@ -58,35 +89,61 @@ fn assert_fidelity(label: &str, project: &Project) -> (Project, render_probe::Pr
"{label}: compact identities/source are stable"
);
let second = render_probe::compact(&compact_model);
assert_common_omissions(project, &compiled);
assert_eq!(
second.canonical, probe.canonical,
"{label}: deterministic baseline"
);
assert_eq!(
second.compact, probe.compact,
"{label}: compact/reparse is idempotent"
);
assert_eq!(
second.removed_fields, probe.removed_fields,
"{label}: deterministic omission count"
language::format(&compact_model),
flat,
"{label}: flat idempotence"
);
let modular = format_bundle(project);
assert!(
probe.removed_fields >= 10,
"{label}: exercise nontrivial omission, got {}",
probe.removed_fields
modular.files.len() > 1,
"{label}: exercise generated includes"
);
let modular_compiled = compile_bundle(&modular, ProjectLimits::default()).unwrap();
assert_eq!(
modular_compiled.decoded.project, *project,
"{label}: complete modular model"
);
assert_eq!(
modular_compiled.bundle, modular,
"{label}: modular source/IDs are stable"
);
assert_common_omissions(project, &modular_compiled);
assert_eq!(
format_bundle(&compact_model),
modular,
"{label}: flat-to-modular determinism"
);
assert_eq!(
format_bundle(&modular_compiled.decoded.project),
modular,
"{label}: modular idempotence"
);
// Pinned measured compact bytes from the published fidelity-1 stderr.log at
// 2a64344, not a comparison against today's (now compact) canonical writer.
// A small header-layout allowance avoids enforcing incidental blank lines.
let measured_compact_bytes = match label {
"coastal-source3" => 28_465,
"keyed-source5-history" => 12_066,
"state-actions-source5" => 6_673,
"dense40-source6" => 19_342,
"boundary-operations-source6" => 3_660,
"state-negative-zero" => 6_672,
"keyed-present-empty-default" => 12_108,
"coastal-present-empty-artifact-strings" => 28_483,
"boundary-selected-and-unresolved-operations" => 3_806,
_ => panic!("missing independent compact measurement for {label}"),
};
assert!(
probe.compact.len() < probe.canonical.len(),
"{label}: source is smaller"
flat.len() <= measured_compact_bytes + 16,
"{label}: {} bytes exceeds compact evidence budget {}",
flat.len(),
measured_compact_bytes + 16
);
eprintln!(
"fidelity {label}: {} -> {} bytes; {} fields removed",
probe.canonical.len(),
probe.compact.len(),
probe.removed_fields
);
(compact_model, probe)
(compact_model, flat)
}
#[test]
@@ -150,9 +207,9 @@ fn keyed_scenarios_records_and_historical_binding_annotation_facets_roundtrip()
KEYED.contains("uuid \""),
"fixture contains historical identity atoms"
);
let (_, probe) = assert_fidelity("keyed-source5-history", &project);
let (_, source) = assert_fidelity("keyed-source5-history", &project);
assert!(
probe.compact.contains("uuid \""),
source.contains("uuid \""),
"historical atoms remain historical"
);
}
@@ -286,16 +343,20 @@ fn negative_zero_node_coordinate_survives_canonical_compact_and_bundle_compile()
.get_mut(&node)
.unwrap()
.position = Position { x: -0.0, y: 0.0 };
let (decoded, probe) = assert_fidelity("state-negative-zero", &project);
let canonical = read(&probe.canonical);
let (decoded, flat) = assert_fidelity("state-negative-zero", &project);
let bundle = SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([("project.arcadia".into(), probe.compact)]),
files: BTreeMap::from([("project.arcadia".into(), flat)]),
};
let compiled = compile_bundle(&bundle, ProjectLimits::default()).unwrap();
let modular = compile_bundle(&format_bundle(&project), ProjectLimits::default()).unwrap();
// Project/Position PartialEq alone equates the signs of zero. Check bits in
// each independently decoded representation, including source-project compile.
for actual in [&canonical, &decoded, &compiled.decoded.project] {
for actual in [
&decoded,
&compiled.decoded.project,
&modular.decoded.project,
] {
let position = &actual.diagrams[&diagram].nodes[&node].position;
assert_eq!(position.x.to_bits(), (-0.0_f32).to_bits());
assert_eq!(position.y.to_bits(), 0.0_f32.to_bits());
@@ -0,0 +1,256 @@
//! Focused public-output contracts. Field locations come from the real compiler;
//! token comparisons ignore layout while retaining typed tags and required syntax.
use oa_core::{language, source_project::*, *};
use std::collections::BTreeMap;
fn project(declarations: &str) -> Project {
language::parse(&format!(
"arcadia 7; project p \"P\" id \"00000000-0000-4000-8000-000000000001\" {{ {declarations} }}"
)).unwrap()
}
fn exports(project: &Project) -> Vec<CompiledSourceProject> {
let flat = language::format(project);
assert_eq!(language::parse(&flat).unwrap(), *project);
let flat_bundle = SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([("project.arcadia".into(), flat)]),
};
[flat_bundle, format_bundle(project)]
.into_iter()
.map(|bundle| {
let compiled = compile_bundle(&bundle, ProjectLimits::default()).unwrap();
assert_eq!(compiled.decoded.project, *project);
assert_eq!(
compiled.bundle, bundle,
"all identities are explicit and stable"
);
compiled
})
.collect()
}
fn identity(project: &Project, alias: &str) -> Id {
project
.identities()
.into_iter()
.find(|(_, name)| *name == alias)
.unwrap()
.0
}
fn field(compiled: &CompiledSourceProject, alias: &str, name: &str) -> Option<String> {
let location = compiled
.index
.fields
.get(&(identity(&compiled.decoded.project, alias), name.into()))?;
let text = &compiled.bundle.files[&location.path][location.span.start..location.span.end];
Some(
language::tokenize(text)
.into_iter()
.filter(|token| {
!matches!(
token.class,
language::TokenClass::Whitespace | language::TokenClass::Comment
)
})
.map(|token| &text[token.span.start..token.span.end])
.collect::<Vec<_>>()
.join(" "),
)
}
#[test]
fn empty_project_has_no_synthetic_description_or_module() {
let empty = project("");
for compiled in exports(&empty) {
assert!(compiled.index.project_description.is_none());
assert!(compiled.index.fields.is_empty());
assert!(compiled.index.includes.is_empty());
assert_eq!(compiled.bundle.files.len(), 1);
assert_eq!(compiled.index.declarations.len(), 1);
}
}
#[test]
fn only_empty_descriptions_disappear_whitespace_and_text_survive_exactly() {
let mut model =
project("oa_activity work \"Work\" id \"00000000-0000-4000-8000-000000000002\" {}");
let work = identity(&model, "work");
for description in ["", " \t\n", "A quoted \"requirement\" — 雨"] {
model.description = description.into();
model.elements.get_mut(&work).unwrap().description = description.into();
for compiled in exports(&model) {
assert_eq!(
compiled.index.project_description.is_some(),
!description.is_empty()
);
assert_eq!(
field(&compiled, "work", "description").is_some(),
!description.is_empty()
);
assert!(field(&compiled, "work", "parent").is_none());
// exports compares the complete model, including exact whitespace
// and escapes in both project and element descriptions.
}
}
}
#[test]
fn sa_view_keeps_its_layer_and_node_references_but_omits_presentation_defaults() {
let model = project(
r#"
sa_function work "Work" id "00000000-0000-4000-8000-000000000002" {}
diagram view "View" id "00000000-0000-4000-8000-000000000003" { layer Sa; }
node placement id "00000000-0000-4000-8000-000000000004" {
diagram @view; element @work;
position { x 0.0; y 0.0; }; size { width 224.0; height 94.0; };
collapsed false; port_anchors [];
}
"#,
);
for compiled in exports(&model) {
assert_eq!(field(&compiled, "view", "layer").as_deref(), Some("Sa"));
for name in ["description", "projection", "routes", "view_options"] {
assert!(field(&compiled, "view", name).is_none(), "view.{name}");
}
assert_eq!(
field(&compiled, "placement", "diagram").as_deref(),
Some("@ view")
);
assert_eq!(
field(&compiled, "placement", "element").as_deref(),
Some("@ work")
);
for name in ["position", "size", "collapsed", "port_anchors"] {
assert!(
field(&compiled, "placement", name).is_none(),
"placement.{name}"
);
}
}
}
#[test]
fn nested_type_defaults_disappear_without_losing_required_or_present_empty_values() {
let model = project(
r#"
sa_data_type implicit "Implicit" id "00000000-0000-4000-8000-000000000002" {}
sa_data_type integer "Integer" id "00000000-0000-4000-8000-000000000003" {
definition { Integer { minimum none; maximum none; }; };
}
sa_data_type text "Text" id "00000000-0000-4000-8000-000000000004" {
definition { String { minimum_length 4; maximum_length none; }; };
}
sa_data_type record "Record" id "00000000-0000-4000-8000-000000000005" {
definition { Record { supertype none; }; };
}
sa_data_type flag "Flag" id "00000000-0000-4000-8000-000000000006" {
definition Boolean; default { Boolean false; };
}
sa_data_field optional "Optional" id "00000000-0000-4000-8000-000000000007" {
parent @record; minimum 0; maximum none; default [];
}
"#,
);
for compiled in exports(&model) {
assert!(field(&compiled, "implicit", "definition").is_none());
assert_eq!(
field(&compiled, "integer", "definition").as_deref(),
Some("{ Integer { } ; }")
);
assert_eq!(
field(&compiled, "text", "definition").as_deref(),
Some("{ String { minimum_length 4 ; } ; }")
);
assert_eq!(
field(&compiled, "record", "definition").as_deref(),
Some("{ Record { } ; }")
);
assert_eq!(
field(&compiled, "flag", "definition").as_deref(),
Some("Boolean")
);
assert_eq!(
field(&compiled, "flag", "default").as_deref(),
Some("{ Boolean false ; }")
);
assert_eq!(
field(&compiled, "optional", "minimum").as_deref(),
Some("0")
);
assert_eq!(
field(&compiled, "optional", "maximum").as_deref(),
Some("none")
);
assert_eq!(
field(&compiled, "optional", "default").as_deref(),
Some("[ ]")
);
assert!(field(&compiled, "optional", "type_ref").is_none());
}
}
#[test]
fn required_port_contract_and_empty_item_list_survive_while_optional_interface_disappears() {
let model = project(
r#"
sa_function work "Work" id "00000000-0000-4000-8000-000000000002" {}
sa_port sink "Sink" id "00000000-0000-4000-8000-000000000003" {
parent @work; contract { FunctionInput { incoming_items []; }; };
}
sa_system host "Host" id "00000000-0000-4000-8000-000000000004" {}
sa_port preserved "Preserved" id "00000000-0000-4000-8000-000000000005" {
parent @host; contract Legacy; legacy { direction Out; interface none; };
}
"#,
);
for compiled in exports(&model) {
assert_eq!(
field(&compiled, "sink", "contract").as_deref(),
Some("{ FunctionInput { incoming_items [ ] ; } ; }")
);
assert!(field(&compiled, "sink", "legacy").is_none());
assert_eq!(
field(&compiled, "preserved", "contract").as_deref(),
Some("Legacy")
);
assert_eq!(
field(&compiled, "preserved", "legacy").as_deref(),
Some("{ direction Out ; }")
);
}
}
#[test]
fn source_scenario_default_and_selected_occurrence_constructor_remain_distinct() {
let model = project(
r#"
sa_scenario mixed "Mixed" id "00000000-0000-4000-8000-000000000002" { viewpoint MixedLegacy; }
sa_scenario strict "Strict" id "00000000-0000-4000-8000-000000000003" { viewpoint Functional; }
oa_process flow "Flow" id "00000000-0000-4000-8000-000000000004" { steps [@exchange, @action]; }
oa_occurrence exchange "Exchange" id "00000000-0000-4000-8000-000000000005" {
parent @flow; body { Exchange {}; };
}
oa_occurrence action "Action" id "00000000-0000-4000-8000-000000000006" {
parent @flow; body { Action { action none; lifeline none; }; };
}
"#,
);
for compiled in exports(&model) {
assert!(field(&compiled, "mixed", "viewpoint").is_none());
assert_eq!(
field(&compiled, "strict", "viewpoint").as_deref(),
Some("Functional")
);
assert!(field(&compiled, "exchange", "body").is_none());
assert_eq!(
field(&compiled, "action", "body").as_deref(),
Some("{ Action { } ; }")
);
assert_eq!(
field(&compiled, "action", "parent").as_deref(),
Some("@ flow")
);
}
}
@@ -1,9 +1,8 @@
//! Exact-source regressions for fields skipped by persistence serialization.
//!
//! The ignored tests assert the desired behavior, not the observed defect.
//! Windows baseline-1 confirmed all three failures at exact-source comparison
//! after full model equality passed. Run with `--ignored` to recheck them, then
//! remove the ignores when the preservation fix lands.
//! The archived Windows baseline confirmed three exact-source failures after
//! full model equality passed. Complete comparison now preserves these fields;
//! the former ignored witnesses are normal regression tests.
//! No-op and same-record header controls remain normal tests: they must preserve
//! the same fixtures without entering the changed-payload record-erasure branch.
@@ -381,7 +380,6 @@ fn explicit_exchange_operation_none_survives_header_rename() {
}
#[test]
#[ignore = "Windows baseline-1 confirmed: Exchange binding edit erases explicit operation none despite correct full model; run --ignored until preservation is fixed"]
fn explicit_exchange_operation_none_survives_exchange_binding_edit() {
let mut expected = exchange_project();
let original = checked_fixture(EXCHANGE_SOURCE, &expected);
@@ -426,7 +424,6 @@ fn explicit_node_empty_port_anchors_survive_header_alias_rename() {
}
#[test]
#[ignore = "Windows baseline-1 confirmed: Node position edit erases explicit port_anchors [] despite correct full model; run --ignored until preservation is fixed"]
fn explicit_node_empty_port_anchors_survive_position_edit() {
let mut expected = node_project();
let original = checked_fixture(NODE_SOURCE, &expected);
@@ -460,7 +457,6 @@ fn explicit_trace_semantic_none_survives_header_rename() {
}
#[test]
#[ignore = "Windows baseline-1 confirmed: snapshot prose edit erases explicit semantic none despite correct full model; run --ignored until preservation is fixed"]
fn explicit_trace_semantic_none_survives_snapshot_prose_edit() {
let mut expected = trace_project();
let original = checked_fixture(TRACE_SOURCE, &expected);
@@ -0,0 +1,222 @@
//! Public update witnesses for compact insertion and complete comparison.
use oa_core::{language, source_project::*, *};
use std::collections::BTreeMap;
fn compile(body: &str) -> CompiledSourceProject {
compile_bundle(
&SourceBundle {
entry: "project.arcadia".into(),
files: BTreeMap::from([
(
"project.arcadia".into(),
"arcadia 7; project p \"P\" { include \"model.arcadia\"; }".into(),
),
("model.arcadia".into(), body.into()),
]),
},
ProjectLimits::default(),
)
.unwrap()
}
fn id(project: &Project, alias: &str) -> Id {
project
.identities()
.into_iter()
.find(|(_, a)| *a == alias)
.unwrap()
.0
}
fn update(before: &CompiledSourceProject, project: &Project) -> CompiledSourceProject {
let result = update_bundle(before, project, ProjectLimits::default()).unwrap();
assert_eq!(result.decoded.project, *project);
assert_eq!(
result.bundle.files["project.arcadia"],
before.bundle.files["project.arcadia"]
);
assert_eq!(
compile_bundle(&result.bundle, ProjectLimits::default())
.unwrap()
.bundle,
result.bundle
);
assert_eq!(
update_bundle(&result, project, ProjectLimits::default())
.unwrap()
.bundle,
result.bundle
);
result
}
fn words(text: &str) -> Vec<&str> {
language::tokenize(text)
.into_iter()
.filter(|t| {
!matches!(
t.class,
language::TokenClass::Whitespace | language::TokenClass::Comment
)
})
.map(|t| &text[t.span.start..t.span.end])
.collect()
}
#[test]
fn omitted_exchange_body_changes_to_one_compact_action_constructor() {
let before = compile(
"oa_scenario s \"S\" { steps [@o]; }\noa_occurrence o \"O\" { parent @s; // keep this\n}\n",
);
let mut project = before.decoded.project.clone();
let occurrence = id(&project, "o");
let Some(SharedBody::Occurrence { body, .. }) =
project.elements.get_mut(&occurrence).unwrap().shared_mut()
else {
panic!("occurrence")
};
assert!(matches!(body, OccurrenceBody::Exchange { .. }));
*body = OccurrenceBody::Action {
action: None,
lifeline: None,
};
let result = update(&before, &project);
let text = &result.bundle.files["model.arcadia"];
let tokens = words(text);
assert_eq!(tokens.iter().filter(|&&t| t == "body").count(), 1);
assert!(tokens.contains(&"Action"));
for absent in ["Exchange", "action", "lifeline", "operation", "description"] {
assert!(!tokens.contains(&absent), "{absent}: {text}");
}
assert!(text.contains("parent @s; // keep this"));
}
#[test]
fn default_signal_constructor_still_inserts_its_required_empty_items() {
let before =
compile("oa_event e \"E\" { body { Time { expression \"1 s\"; kind After; }; }; }\n");
let mut project = before.decoded.project.clone();
let event = id(&project, "e");
let Some(SharedBody::Event { body }) = project.elements.get_mut(&event).unwrap().shared_mut()
else {
panic!("event")
};
*body = EventBody::Signal { items: vec![] };
let result = update(&before, &project);
let tokens = words(&result.bundle.files["model.arcadia"]);
assert!(tokens.contains(&"Signal"));
assert!(tokens.windows(4).any(|w| w == ["items", "[", "]", ";"]));
assert!(!tokens.contains(&"expression"));
assert!(!tokens.contains(&"kind"));
}
#[test]
fn scalar_to_default_string_record_retains_required_minimum_only() {
let before = compile("oa_data_type t \"T\" { definition Boolean; // typed rationale\n}\n");
let mut project = before.decoded.project.clone();
let datatype = id(&project, "t");
let Some(SharedBody::DataType { definition, .. }) =
project.elements.get_mut(&datatype).unwrap().shared_mut()
else {
panic!("data type")
};
*definition = DataTypeDefinition::String {
minimum_length: 0,
maximum_length: None,
};
let result = update(&before, &project);
let text = &result.bundle.files["model.arcadia"];
let tokens = words(text);
assert!(tokens.contains(&"String"));
assert!(tokens.windows(3).any(|w| w == ["minimum_length", "0", ";"]));
assert!(!tokens.contains(&"maximum_length"));
assert!(text.contains("// typed rationale"));
}
#[test]
fn inserted_route_list_item_omits_only_optional_fields() {
let before = compile(
"oa_activity a \"A\" {}\noa_activity b \"B\" {}\ninteraction flow \"Flow\" { source @a; target @b; }\ndiagram d \"D\" { routes []; // routing rationale\n}\n",
);
let mut project = before.decoded.project.clone();
let diagram = id(&project, "d");
let edge = id(&project, "flow");
// Deserialize only the explicit witness value, independently of omission policy.
project.diagrams.get_mut(&diagram).unwrap().routes =
serde_json::from_value(serde_json::json!([{"edge":edge,"waypoints":[],"label":null}]))
.unwrap();
let result = update(&before, &project);
let text = &result.bundle.files["model.arcadia"];
let tokens = words(text);
assert!(tokens.windows(4).any(|w| w == ["edge", "@", "flow", ";"]));
assert!(!tokens.contains(&"waypoints"));
assert!(!tokens.contains(&"label"));
assert!(text.contains("// routing rationale"));
}
#[test]
fn zero_sign_change_inserts_position_without_default_siblings() {
let before = compile(
"oa_activity a \"A\" {}\ndiagram d \"D\" {}\nnode n { diagram @d; element @a; // placement rationale\n}\n",
);
let mut project = before.decoded.project.clone();
let diagram = id(&project, "d");
let node = id(&project, "n");
assert_eq!(
project.diagrams[&diagram].nodes[&node].position.x.to_bits(),
0.0_f32.to_bits()
);
project
.diagrams
.get_mut(&diagram)
.unwrap()
.nodes
.get_mut(&node)
.unwrap()
.position
.x = -0.0;
let result = update(&before, &project);
assert_eq!(
result.decoded.project.diagrams[&diagram].nodes[&node]
.position
.x
.to_bits(),
(-0.0_f32).to_bits()
);
let text = &result.bundle.files["model.arcadia"];
let tokens = words(text);
assert!(tokens.windows(3).any(|w| w == ["x", "-0.0", ";"]));
for absent in ["size", "collapsed", "port_anchors"] {
assert!(!tokens.contains(&absent));
}
assert!(text.contains("// placement rationale"));
}
#[test]
fn kind_change_materializes_unchanged_value_when_its_old_omission_default_changes() {
let before = compile(
"oa_entity a \"A\" {}\noa_entity b \"B\" {}\noa_port out \"Out\" { parent @a; contract Legacy; legacy { direction Out; }; }\noa_port input \"In\" { parent @b; contract Legacy; legacy { direction In; }; }\nboundary_exchange flow \"Flow\" { source @out; target @input; // orientation inherited\n}\n",
);
let mut project = before.decoded.project.clone();
let flow = id(&project, "flow");
assert!(matches!(
project.relations[&flow].body,
RelationBody::BoundaryExchange { oriented: true, .. }
));
project.relations.get_mut(&flow).unwrap().body = RelationBody::CommunicationMean {
source: id(&before.decoded.project, "a"),
target: id(&before.decoded.project, "b"),
oriented: true,
items: vec![],
};
let result = update(&before, &project);
let text = &result.bundle.files["model.arcadia"];
assert!(
words(text)
.windows(3)
.any(|w| w == ["oriented", "true", ";"])
);
assert!(text.contains("// orientation inherited"));
let before_ports = &before.bundle.files["model.arcadia"];
assert_eq!(
text.split("communication_mean").next().unwrap(),
before_ports.split("boundary_exchange").next().unwrap()
);
}
+9 -1
View File
@@ -767,7 +767,15 @@ fn resetting_last_source_anchors_preserves_comments_and_can_add_a_new_manual_rec
assert_eq!(cleared.decoded.project, prepared.project);
let text = &cleared.bundle.files["project.arcadia"];
assert!(text.contains("// first rationale") && text.contains("// second rationale"));
assert!(!text.contains("port_anchors"));
// Lossless editing may retain the authored empty field and its annotations.
// Canonical export, rather than a model update, normalizes default syntax.
assert!(!language::format(&cleared.decoded.project).contains("port_anchors"));
assert_eq!(
update_bundle(&cleared, &cleared.decoded.project, ProjectLimits::default())
.unwrap()
.bundle,
cleared.bundle
);
let prepared = prepare_command(
&cleared.decoded.project,
&Command::SetPortAnchor {
+53 -16
View File
@@ -2,7 +2,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<meta name="generator" content="Docutils 0.22.2: https://docutils.sourceforge.io/" />
<meta name="generator" content="Docutils 0.23: https://docutils.sourceforge.io/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Arcadia Studio OA / SA Language and Editing Contract</title>
<style type="text/css">
@@ -806,11 +806,11 @@ references may be omitted and then mean <span class="docutils literal">none</spa
These omissions are accepted in newly authored source 7, not only in schema-2
compatibility input. Earlier schema admission of constructors and fields is
still governed by its version rules.</p>
<p>Canonical formatting writes the hydrated occurrence references and default
<span class="docutils literal">message_kind</span>, <span class="docutils literal">reply_to</span> and <span class="docutils literal">values</span> explicitly. An absent
<span class="docutils literal">operation</span> remains omitted in canonical output. Lossless no-op updates and
unrelated name edits retain authored omissions; canonical formatting is a
different operation. An admitted incomplete occurrence can still produce model
<p>Canonical formatting omits optional occurrence references equal to <span class="docutils literal">none</span>,
the default Asynchronous <span class="docutils literal">message_kind</span>, empty <span class="docutils literal">values</span>, and absent
<span class="docutils literal">operation</span>. Required fields of a selected constructor remain present.
Lossless updates retain unchanged explicit defaults and authored omissions;
canonical formatting is a different operation. An admitted incomplete occurrence can still produce model
checks requesting its missing work, exchange, lifeline or referenced scenario.</p>
<table>
<caption>Occurrence body constructors</caption>
@@ -1569,29 +1569,40 @@ has no explicit default, and does not convey ownership or deletion of the type.<
<thead>
<tr><th class="head"><p>Constructor</p></th>
<th class="head"><p>Required fields</p></th>
<th class="head"><p>Optional fields (omission value)</p></th>
</tr>
</thead>
<tbody>
<tr><td><p>Boolean</p></td>
<td><p>None</p></td>
<td><p>None</p></td>
</tr>
<tr><td><p>Integer</p></td>
<td><p><span class="docutils literal">minimum</span>, <span class="docutils literal">maximum</span>: optional signed 64-bit integers</p></td>
<td><p>None</p></td>
<td><p><span class="docutils literal">minimum</span>, <span class="docutils literal">maximum</span>: signed 64-bit integers? (none)</p></td>
</tr>
<tr><td><p>Real</p></td>
<td><p><span class="docutils literal">minimum</span>, <span class="docutils literal">maximum</span>: optional finite IEEE-754 binary64 values</p></td>
<td><p>None</p></td>
<td><p><span class="docutils literal">minimum</span>, <span class="docutils literal">maximum</span>: finite IEEE-754 binary64 values? (none)</p></td>
</tr>
<tr><td><p>String</p></td>
<td><p><span class="docutils literal">minimum_length</span>: u32; <span class="docutils literal">maximum_length</span>: optional u32</p></td>
<td><p><span class="docutils literal">minimum_length</span>: u32</p></td>
<td><p><span class="docutils literal">maximum_length</span>: u32? (none)</p></td>
</tr>
<tr><td><p>Enumeration</p></td>
<td><p><span class="docutils literal">literals</span>: unique nonempty, case-sensitive strings</p></td>
<td><p>None</p></td>
</tr>
<tr><td><p>Record</p></td>
<td><p><span class="docutils literal">supertype</span>: same-layer Record ref?</p></td>
<td><p>None</p></td>
<td><p><span class="docutils literal">supertype</span>: same-layer Record ref? (none)</p></td>
</tr>
</tbody>
</table>
<p>Optional fields may be absent or explicitly <span class="docutils literal">none</span>. Selected String and
Enumeration constructors still require <span class="docutils literal">minimum_length</span> and <span class="docutils literal">literals</span>,
respectively, including zero and an empty list. Selecting a constructor does
not merge fields from the default String definition. Unknown fields reject.</p>
<p>Bounds are inclusive and ordered; an absent bound means unbounded within the
represented numeric domain. Integer comparisons MUST NOT convert to binary64.
NaN, infinities, overflow and reversed bounds reject. String length counts Unicode
@@ -2439,10 +2450,27 @@ references, placements, artifacts, trace baselines, and ordered occurrences.
Map order is irrelevant. The writer preserves signed coordinate zero and
emits decimals that recover the original binary32 value.</p>
<p>Canonical output uses LF, four-space indentation, explicit identities,
defaults, and JSON-escaped strings. Elements are emitted in UUID order, then
and JSON-escaped strings. It omits fields equal to the omission defaults of
their actual declaration or selected constructor, as specified in the field
tables. This includes an empty project description. It MUST NOT omit a required
field merely because its value is zero, false, empty, or <span class="docutils literal">none</span>. Elements are emitted in UUID order, then
relations; diagrams and their nodes follow, then artifacts, then traces.
Fields use deterministic name order. Output ends with LF. Alias values come
from the model, never from regenerating display names.</p>
<p>Omission is context-sensitive: Scenario uses the source default MixedLegacy;
BoundaryExchange and CommunicationMean have different orientation defaults;
state declarations determine their state-kind discriminator. A nonempty or
whitespace-only description remains exact. Present-empty optional strings or
collections, unbounded multiplicity versus a finite default, selected operation
intent, and negative coordinate zero remain distinct values. A nondefault
geometry record retains all required coordinates or dimensions.</p>
<p>Only specified optional fields inside selected constructors may be omitted.
For example, Integer bounds and LegacyPortContract.interface may be absent;
String.minimum_length, Signal.items, port-contract payload lists, and historical
snapshot name/description fields remain required. A LegacyPortContract record
still requires direction. Historical semantic baseline payloads are retained
without generic empty/default pruning. Compact output MUST preserve every
persistent model field after parsing, including these distinctions.</p>
<p>Comments and input whitespace are lexical trivia to the canonical writer.
Canonical export does not preserve them. The source-project editor and compiler
preserve authored comments and whitespace on Apply while inserting missing
@@ -2484,11 +2512,19 @@ independent parent and child moves are resolved together. Leading whitespace
adjusts to the destination scope; other token spelling remains authored.</p>
<p>Updates MUST NOT canonicalize surviving declarations in full. Header, field and
nested value positions are derived from the existing syntax parser. Unchanged
fields retain their exact bytes, including field order, omitted defaults, number
fields retain their exact bytes, including field order, explicit and omitted defaults, number
and string spelling, comments, indentation and line endings. Changed literals and
references replace only the required tokens; a reference with comments between
<span class="docutils literal">&#64;</span> and its alias retains those comments. A previously omitted default is
inserted only if its semantic value changes. New fields use the existing record's
<span class="docutils literal">&#64;</span> and its alias retains those comments. A previously omitted field is
inserted only when needed to express the requested value in its new declaration
context. This includes an unchanged value whose omission default changes with
the declaration kind. For example, changing BoundaryExchange to CommunicationMean
while retaining orientation true requires <span class="docutils literal">oriented true;</span> if previously
implicit. Unchanged explicit defaults MUST survive unrelated sibling edits,
including persistence-serializer omissions such as <span class="docutils literal">operation none</span>,
<span class="docutils literal">port_anchors []</span> and <span class="docutils literal">baseline.semantic none</span>. New fields and value fragments
use compact generation while retaining required selected-constructor payloads.
New fields use the existing record's
indentation and line-ending style without reformatting its other fields.</p>
<p>A constructor tag change retains shared nested fields in place. Removed fields,
constructors and declarations remove syntax tokens, retaining their comments
@@ -2770,8 +2806,9 @@ This compatibility applies equally to source and JSON. It adds explicit
defaults without changing any prior identity, name, reference, order, geometry,
artifact or trace field. The original V2 golden source/JSON in
<span class="docutils literal"><span class="pre">crates/oa-core/tests/fixtures</span></span> MUST NOT be regenerated from the newer demo;
they verify that every old persistent field survives. Canonical output writes
the added defaults through explicit migration to the current schema 7.</p>
they verify that every old persistent field survives. Canonical output uses
the current schema 7 and omits values equal to its specified source defaults;
parsing recovers the complete migrated model.</p>
<p>The V2 adapter preserves every original identity, alias, prose, parent,
placement, trace and artifact. Generic ports become Legacy with their original
direction/interface record. Existing FE endpoint IDs remain unchanged. For
+55 -15
View File
@@ -171,11 +171,11 @@ These omissions are accepted in newly authored source 7, not only in schema-2
compatibility input. Earlier schema admission of constructors and fields is
still governed by its version rules.
Canonical formatting writes the hydrated occurrence references and default
``message_kind``, ``reply_to`` and ``values`` explicitly. An absent
``operation`` remains omitted in canonical output. Lossless no-op updates and
unrelated name edits retain authored omissions; canonical formatting is a
different operation. An admitted incomplete occurrence can still produce model
Canonical formatting omits optional occurrence references equal to ``none``,
the default Asynchronous ``message_kind``, empty ``values``, and absent
``operation``. Required fields of a selected constructor remain present.
Lossless updates retain unchanged explicit defaults and authored omissions;
canonical formatting is a different operation. An admitted incomplete occurrence can still produce model
checks requesting its missing work, exchange, lifeline or referenced scenario.
.. list-table:: Occurrence body constructors
@@ -933,18 +933,30 @@ DataTypeDefinition has exactly the following constructors:
* - Constructor
- Required fields
- Optional fields (omission value)
* - Boolean
- None
- None
* - Integer
- ``minimum``, ``maximum``: optional signed 64-bit integers
- None
- ``minimum``, ``maximum``: signed 64-bit integers? (none)
* - Real
- ``minimum``, ``maximum``: optional finite IEEE-754 binary64 values
- None
- ``minimum``, ``maximum``: finite IEEE-754 binary64 values? (none)
* - String
- ``minimum_length``: u32; ``maximum_length``: optional u32
- ``minimum_length``: u32
- ``maximum_length``: u32? (none)
* - Enumeration
- ``literals``: unique nonempty, case-sensitive strings
- None
* - Record
- ``supertype``: same-layer Record ref?
- None
- ``supertype``: same-layer Record ref? (none)
Optional fields may be absent or explicitly ``none``. Selected String and
Enumeration constructors still require ``minimum_length`` and ``literals``,
respectively, including zero and an empty list. Selecting a constructor does
not merge fields from the default String definition. Unknown fields reject.
Bounds are inclusive and ordered; an absent bound means unbounded within the
represented numeric domain. Integer comparisons MUST NOT convert to binary64.
@@ -1840,11 +1852,30 @@ Map order is irrelevant. The writer preserves signed coordinate zero and
emits decimals that recover the original binary32 value.
Canonical output uses LF, four-space indentation, explicit identities,
defaults, and JSON-escaped strings. Elements are emitted in UUID order, then
and JSON-escaped strings. It omits fields equal to the omission defaults of
their actual declaration or selected constructor, as specified in the field
tables. This includes an empty project description. It MUST NOT omit a required
field merely because its value is zero, false, empty, or ``none``. Elements are emitted in UUID order, then
relations; diagrams and their nodes follow, then artifacts, then traces.
Fields use deterministic name order. Output ends with LF. Alias values come
from the model, never from regenerating display names.
Omission is context-sensitive: Scenario uses the source default MixedLegacy;
BoundaryExchange and CommunicationMean have different orientation defaults;
state declarations determine their state-kind discriminator. A nonempty or
whitespace-only description remains exact. Present-empty optional strings or
collections, unbounded multiplicity versus a finite default, selected operation
intent, and negative coordinate zero remain distinct values. A nondefault
geometry record retains all required coordinates or dimensions.
Only specified optional fields inside selected constructors may be omitted.
For example, Integer bounds and LegacyPortContract.interface may be absent;
String.minimum_length, Signal.items, port-contract payload lists, and historical
snapshot name/description fields remain required. A LegacyPortContract record
still requires direction. Historical semantic baseline payloads are retained
without generic empty/default pruning. Compact output MUST preserve every
persistent model field after parsing, including these distinctions.
Comments and input whitespace are lexical trivia to the canonical writer.
Canonical export does not preserve them. The source-project editor and compiler
preserve authored comments and whitespace on Apply while inserting missing
@@ -1890,11 +1921,19 @@ adjusts to the destination scope; other token spelling remains authored.
Updates MUST NOT canonicalize surviving declarations in full. Header, field and
nested value positions are derived from the existing syntax parser. Unchanged
fields retain their exact bytes, including field order, omitted defaults, number
fields retain their exact bytes, including field order, explicit and omitted defaults, number
and string spelling, comments, indentation and line endings. Changed literals and
references replace only the required tokens; a reference with comments between
``@`` and its alias retains those comments. A previously omitted default is
inserted only if its semantic value changes. New fields use the existing record's
``@`` and its alias retains those comments. A previously omitted field is
inserted only when needed to express the requested value in its new declaration
context. This includes an unchanged value whose omission default changes with
the declaration kind. For example, changing BoundaryExchange to CommunicationMean
while retaining orientation true requires ``oriented true;`` if previously
implicit. Unchanged explicit defaults MUST survive unrelated sibling edits,
including persistence-serializer omissions such as ``operation none``,
``port_anchors []`` and ``baseline.semantic none``. New fields and value fragments
use compact generation while retaining required selected-constructor payloads.
New fields use the existing record's
indentation and line-ending style without reformatting its other fields.
A constructor tag change retains shared nested fields in place. Removed fields,
@@ -2207,8 +2246,9 @@ This compatibility applies equally to source and JSON. It adds explicit
defaults without changing any prior identity, name, reference, order, geometry,
artifact or trace field. The original V2 golden source/JSON in
``crates/oa-core/tests/fixtures`` MUST NOT be regenerated from the newer demo;
they verify that every old persistent field survives. Canonical output writes
the added defaults through explicit migration to the current schema 7.
they verify that every old persistent field survives. Canonical output uses
the current schema 7 and omits values equal to its specified source defaults;
parsing recovers the complete migrated model.
The V2 adapter preserves every original identity, alias, prose, parent,
placement, trace and artifact. Generic ports become Legacy with their original
@@ -1,43 +1,34 @@
oa_activity oa_restore_work "Restore safe pressure" id "1b91f8a7-0bdb-514e-b1cd-955658e09927" {
description "";
parent none;
}
oa_activity oa_assess "Assess pressure alarm" id "61f7bbfb-74a4-5540-9182-2583dfb27a49" {
description "";
parent @oa_restore_work;
}
oa_activity oa_confirm "Confirm safe pressure" id "727c0b5c-8db6-5eb7-83d6-735519c5949f" {
description "";
parent @oa_restore_work;
}
oa_activity oa_repair "Repair valve in the field" id "a21465af-05f3-5fb5-8983-de26932422fb" {
description "";
parent @oa_restore_work;
}
oa_activity oa_reset "Reset valve remotely" id "d0c8be87-d238-503a-a549-7558a210507f" {
description "";
parent @oa_restore_work;
}
capability_involvement oa_assessment_support "Capability involvement: Assess an alarm" id "ffdbfcd0-d423-5707-a880-0dfbe1400f72" {
description "";
source @oa_assess;
target @oa_triage;
}
interaction oa_repair_request "Request field repair" id "cf423ea2-8b2e-5a27-93d4-5e8542d9db8d" {
description "";
items [@oa_repair_operation];
source @oa_assess;
target @oa_repair;
}
interaction oa_repair_report "Report repair result" id "b436d0b3-c824-5a96-af6b-2c269816d1cc" {
description "";
items [@oa_repair_operation];
source @oa_repair;
target @oa_confirm;
@@ -1,6 +1,4 @@
oa_process oa_assessment_procedure "Assess an alarm procedure" id "022d3cfa-bbaf-5e13-924a-8e55099b8745" {
description "";
parent none;
steps [@oa_assessment_action];
}
@@ -8,17 +6,12 @@ oa_occurrence oa_assessment_action "Assess pressure alarm" id "942975b8-0bef-5ae
body {
Action {
action @oa_assess;
lifeline none;
};
};
description "";
parent @oa_assessment_procedure;
postcondition "";
precondition "";
}
chain_involvement oa_assessment_delivery "Chain involvement: Assess an alarm" id "628cb4fc-fe63-5f6b-b364-6268f26b5740" {
description "";
source @oa_assessment_procedure;
target @oa_triage;
}
@@ -1,6 +1,4 @@
oa_process oa_field_procedure "Field repair procedure" id "5d3af417-244c-54bb-9588-16daedda4e1f" {
description "";
parent none;
steps [@oa_request_step, @oa_repair_step, @oa_report_step];
}
@@ -8,59 +6,35 @@ oa_occurrence oa_repair_step "Repair the valve" id "c59432fe-6092-5ee0-908d-9dbf
body {
Action {
action @oa_repair;
lifeline none;
};
};
description "";
parent @oa_field_procedure;
postcondition "";
precondition "";
}
oa_occurrence oa_report_step "Report the measured result" id "df2fd3f8-6bc2-5380-a13f-36643ed35ac7" {
body {
Exchange {
exchange @oa_repair_report;
message_kind Asynchronous;
receiver none;
reply_to none;
sender none;
values [];
};
};
description "";
parent @oa_field_procedure;
postcondition "";
precondition "";
}
oa_occurrence oa_request_step "Request the field repair" id "5ba8079c-2dbe-5e98-ae82-2b60c6020a02" {
body {
Exchange {
exchange @oa_repair_request;
message_kind Asynchronous;
receiver none;
reply_to none;
sender none;
values [];
};
};
description "";
parent @oa_field_procedure;
postcondition "";
precondition "";
}
sequence_link oa_repair_then_report "Sequence link: Report the measured result" id "c8196d35-2168-5fef-8b03-ba0d139bc00f" {
description "";
guard "";
source @oa_repair_step;
target @oa_report_step;
}
sequence_link oa_request_then_repair "Sequence link: Repair the valve" id "3f54d890-4ca5-5e36-873b-abb5c92d578c" {
description "";
guard "";
source @oa_request_step;
target @oa_repair_step;
}
@@ -1,6 +1,4 @@
oa_process oa_response "Handle a pressure alarm" id "58bae2b0-f560-50f2-818d-19091d22d18a" {
description "";
parent none;
steps [@oa_assess_step, @oa_decision, @oa_reset_step, @oa_use_field, @oa_merge, @oa_confirm_step];
}
@@ -10,23 +8,16 @@ oa_occurrence oa_assess_step "Assess the alarm" id "0b07b9ed-492d-57f6-b0b1-ce8a
sequence @oa_assessment_procedure;
};
};
description "";
parent @oa_response;
postcondition "";
precondition "";
}
oa_occurrence oa_confirm_step "Confirm safe pressure" id "e00594a3-6122-5a6b-b3c2-ecb5506c741d" {
body {
Action {
action @oa_confirm;
lifeline none;
};
};
description "";
parent @oa_response;
postcondition "";
precondition "";
}
oa_occurrence oa_decision "Can a remote reset safely recover pressure?" id "b280c6ca-e5f9-5424-a5c1-5676e01c2371" {
@@ -35,10 +26,7 @@ oa_occurrence oa_decision "Can a remote reset safely recover pressure?" id "b280
kind Choice;
};
};
description "";
parent @oa_response;
postcondition "";
precondition "";
}
oa_occurrence oa_merge "Recovery path completed" id "6b945a6d-6aa0-5dd3-96e6-2e8d757d167d" {
@@ -47,23 +35,16 @@ oa_occurrence oa_merge "Recovery path completed" id "6b945a6d-6aa0-5dd3-96e6-2e8
kind Merge;
};
};
description "";
parent @oa_response;
postcondition "";
precondition "";
}
oa_occurrence oa_reset_step "Try the authorized remote reset" id "d2026fc2-74a8-5e22-9942-ff3eb68a3677" {
body {
Action {
action @oa_reset;
lifeline none;
};
};
description "";
parent @oa_response;
postcondition "";
precondition "";
}
oa_occurrence oa_use_field "Use the field repair procedure" id "fc6763bf-a534-54bf-afcd-57ab285ff07d" {
@@ -72,56 +53,42 @@ oa_occurrence oa_use_field "Use the field repair procedure" id "fc6763bf-a534-54
sequence @oa_field_procedure;
};
};
description "";
parent @oa_response;
postcondition "";
precondition "";
}
sequence_link oa_assess_then_decide "Continue" id "109e2d6b-7e86-5934-93a5-fab56eced84e" {
description "";
guard "";
source @oa_assess_step;
target @oa_decision;
}
sequence_link oa_field_path "else" id "c1adbbd0-a762-5ee3-8b9a-5c18af377771" {
description "";
guard "else";
source @oa_decision;
target @oa_use_field;
}
sequence_link oa_remote_path "remote reset is authorized" id "76218d89-68fd-531d-b35b-90c5b769a5ef" {
description "";
guard "remote reset is authorized";
source @oa_decision;
target @oa_reset_step;
}
sequence_link oa_confirm_path "Continue" id "229ee94f-d147-50f6-9769-b5bd0c03a084" {
description "";
guard "";
source @oa_merge;
target @oa_confirm_step;
}
sequence_link oa_remote_merge "Continue" id "bac43d3b-910b-5bdc-be9b-f78cf973e1ed" {
description "";
guard "";
source @oa_reset_step;
target @oa_merge;
}
chain_involvement oa_response_delivery "Chain involvement: Restore a safe supply" id "c7c248f8-28b8-55be-9023-0991d44021e3" {
description "";
source @oa_response;
target @oa_restore;
}
sequence_link oa_field_merge "Continue" id "c8593670-8906-530f-b6a2-3ada77a9c219" {
description "";
guard "";
source @oa_use_field;
target @oa_merge;
}
@@ -1,7 +1,5 @@
oa_scenario oa_conversation "Repair request and measured reply" id "20472233-5ce6-5ffe-b4ad-0bdbccf3e423" {
context @oa_restore;
description "";
parent none;
postcondition "The controller has received the measured pressure after repair";
precondition "A pressure alarm has been assessed";
steps [@oa_call, @oa_reply];
@@ -14,23 +12,17 @@ oa_occurrence oa_call "Request valve repair" id "b97437c6-cd7a-5a05-b537-b30a17d
exchange @oa_contact;
message_kind Call;
receiver @oa_responder;
reply_to none;
sender @oa_caller;
values [{
expression "420.0";
field @oa_pressure_parameter;
path [];
}];
};
};
description "";
parent @oa_conversation;
postcondition "";
precondition "";
}
oa_lifeline oa_caller "Controller" id "41859a65-ded2-547b-ab03-14e2fa740cf9" {
description "";
parent @oa_conversation;
represented @oa_control;
}
@@ -46,18 +38,13 @@ oa_occurrence oa_reply "Return pressure after repair" id "cc2d497a-fd15-5455-9ee
values [{
expression "320.0";
field @oa_pressure_parameter;
path [];
}];
};
};
description "";
parent @oa_conversation;
postcondition "";
precondition "";
}
oa_lifeline oa_responder "Technician" id "fc81c5f0-0442-5b88-992b-e5b10f351c59" {
description "";
parent @oa_conversation;
represented @oa_technician;
}
@@ -1,7 +1,5 @@
oa_scenario oa_full_scenario "Restore supply through field repair" id "d6f881a4-95da-54e9-ad97-1b8965532629" {
context @oa_restore;
description "";
parent none;
postcondition "The controller has received the measured pressure after repair";
precondition "A pressure alarm has been assessed";
steps [@oa_use_conversation];
@@ -9,13 +7,11 @@ oa_scenario oa_full_scenario "Restore supply through field repair" id "d6f881a4-
}
oa_lifeline oa_outer_controller "Controller" id "2011b3ac-614b-5a47-8891-7ff81965f425" {
description "";
parent @oa_full_scenario;
represented @oa_control;
}
oa_lifeline oa_outer_technician "Technician" id "fffff882-78fd-5415-928d-c1ef7fac6576" {
description "";
parent @oa_full_scenario;
represented @oa_technician;
}
@@ -33,9 +29,6 @@ oa_occurrence oa_use_conversation "Use the reviewed repair conversation" id "9ff
scenario @oa_conversation;
};
};
description "";
parent @oa_full_scenario;
postcondition "";
precondition "";
}
@@ -1,80 +1,43 @@
oa_state_machine oa_monitor "Pressure supervision" id "1970608f-85d1-5234-ac86-5e2b0df27dc3" {
description "";
parent @oa_control;
}
oa_region oa_monitor_region "Supervision cycle" id "8412e9c6-96fe-5ed6-a903-1d37ec3c3bf7" {
description "";
parent @oa_monitor;
}
oa_initial oa_monitor_initial "Start" id "a7ffb869-9dde-5e76-b276-c719c5c143a9" {
description "";
do_activity "";
entry "";
exit "";
kind Initial;
parent @oa_monitor_region;
}
oa_state oa_monitoring "Monitoring" id "6cbde0d8-9f5f-510a-a8c1-e29cb99ad4ae" {
description "";
do_activity "";
entry "";
exit "";
kind State;
parent @oa_monitor_region;
}
oa_state oa_waiting "Awaiting repair" id "58c15833-6e75-523a-b57f-a88f19f6d672" {
description "";
do_activity "";
entry "";
exit "";
kind State;
parent @oa_monitor_region;
}
state_transition oa_start_monitoring "State transition: Monitoring" id "2b00270c-79a5-52a5-af96-33346bea531b" {
description "";
effect "";
effects [];
guard "";
source @oa_monitor_initial;
target @oa_monitoring;
transition_kind External;
trigger "";
triggers [];
}
state_transition oa_alarm_transition "State transition: Awaiting repair" id "fbde9d5e-c7e9-55db-a2be-497345c491b3" {
description "";
effect "";
effects [];
guard "";
source @oa_monitoring;
target @oa_waiting;
transition_kind External;
trigger "";
triggers [@oa_high_pressure];
}
state_action oa_assess_on_entry "Assess when repair is needed" id "7fa37e63-db65-5aa7-88f2-31ff4347a787" {
description "";
phase Entry;
source @oa_waiting;
target @oa_assess;
}
state_transition oa_normal_transition "State transition: Monitoring" id "21fe5e8e-cbc2-5dad-b3e8-1df46e167d97" {
description "";
effect "";
effects [];
guard "repair confirmed and pressure is safe";
source @oa_waiting;
target @oa_monitoring;
transition_kind External;
trigger "";
triggers [];
}
@@ -1,26 +1,18 @@
oa_mission oa_mission "Maintain safe water supply" id "c9c61f50-06e0-5d17-973d-dcc57cd5cc90" {
description "";
parent none;
}
oa_capability oa_restore "Restore a safe supply" id "3dfee23a-148b-5cec-ad2d-b774e84ea5bb" {
description "";
parent none;
}
oa_capability oa_triage "Assess an alarm" id "6a30b763-cc51-546d-907f-5c63657cfd03" {
description "";
parent none;
}
mission_capability oa_mission_delivery "Mission capability: Restore a safe supply" id "1f8b1898-65f6-5360-99cf-b78a71bd50ec" {
description "";
source @oa_mission;
target @oa_restore;
}
capability_include oa_include_triage "Capability include: Assess an alarm" id "e21d9a54-ae8e-5618-a4f8-67ee22b92638" {
description "";
source @oa_restore;
target @oa_triage;
}
-7
View File
@@ -1,8 +1,6 @@
oa_exchange_category oa_contact_category "Field contact" id "19bd3a3a-4d0c-56d5-bf50-a6b43fd81771" {
description "";
exchanges [@oa_contact];
family "Participant";
parent none;
}
oa_event oa_high_pressure "Pressure alarm detected" id "65d6b8d6-271a-5c84-8b42-abbe310c955e" {
@@ -11,14 +9,9 @@ oa_event oa_high_pressure "Pressure alarm detected" id "65d6b8d6-271a-5c84-8b42-
expression "measured_pressure > operating_limit";
};
};
description "";
parent none;
}
oa_exchange_category oa_repair_traffic "Repair work exchanges" id "9349b353-53f2-56f8-b571-a05b09335cdf" {
description "";
exchanges [@oa_repair_request, @oa_repair_report];
family "Work";
parent none;
}
-13
View File
@@ -1,59 +1,46 @@
oa_entity oa_control "Control room" id "32aa9940-df6e-5a12-b9bd-6337e2d2ed22" {
description "";
parent none;
}
oa_actor oa_technician "Field technician" id "e07c3952-4a1a-5cc7-b7d5-03a01101ce49" {
description "";
parent none;
}
allocation oa_assess_allocation "Allocation: Assess pressure alarm" id "594c61cd-94e0-5c07-890d-edfd520d7df9" {
description "";
source @oa_control;
target @oa_assess;
}
allocation oa_confirm_allocation "Allocation: Confirm safe pressure" id "bfd4326e-ea65-5269-b5e6-e063a355d1e9" {
description "";
source @oa_control;
target @oa_confirm;
}
communication_mean oa_contact "Technician service conversation" id "27b47887-baf0-518b-bf87-f5acbf7d0b93" {
description "";
items [@oa_repair_operation];
oriented false;
source @oa_control;
target @oa_technician;
}
exchange_allocation oa_report_carriage "Exchange allocation: Report repair result" id "4549d3fa-5ab9-52a9-b4f9-3a78fc9d625a" {
description "";
source @oa_contact;
target @oa_repair_report;
}
exchange_allocation oa_request_carriage "Exchange allocation: Request field repair" id "094b888c-450c-5c4f-971a-7d75db059574" {
description "";
source @oa_contact;
target @oa_repair_request;
}
mission_involvement oa_mission_participant "Mission involvement: Maintain safe water supply" id "0c4c9e58-ff32-5c00-b82a-d408a4be0183" {
description "";
source @oa_control;
target @oa_mission;
}
allocation oa_reset_allocation "Allocation: Reset valve remotely" id "1853df1f-30f4-5b1a-baac-2dcaf73c7b3a" {
description "";
source @oa_control;
target @oa_reset;
}
allocation oa_repair_allocation "Allocation: Repair valve in the field" id "ae53f363-0dec-5a7c-9afd-8fcd1b778ab2" {
description "";
source @oa_technician;
target @oa_repair;
}
-13
View File
@@ -1,35 +1,22 @@
oa_data_type oa_pressure "Measured pressure" id "e7824764-5db9-5384-8198-2fbaea2b10b2" {
default none;
definition {
Real {
maximum 600.0;
minimum 0.0;
};
};
description "";
parent none;
unit "kPa";
}
oa_exchange_item oa_repair_operation "Valve repair" id "d736c7d6-d369-550a-8ba4-dae223e40bb3" {
description "";
kind Information;
mechanism Operation;
parent none;
supertype none;
}
oa_data_field oa_pressure_parameter "Pressure before / after repair" id "8bc6d0a4-3e87-559b-8bd7-1b679a2db5b7" {
default none;
description "";
direction InOut;
maximum 1;
minimum 1;
parent @oa_repair_operation;
storage Value;
type_ref {
Named @oa_pressure;
};
unit "";
}
@@ -1,6 +1,4 @@
sa_functional_chain sa_assessment_procedure "Assess an alarm procedure" id "acd5a018-f559-5298-a40d-1cf6e1353e74" {
description "";
parent none;
steps [@sa_assessment_action];
}
@@ -8,17 +6,12 @@ sa_occurrence sa_assessment_action "Assess pressure alarm" id "bae5b192-5b8e-51c
body {
Action {
action @sa_assess;
lifeline none;
};
};
description "";
parent @sa_assessment_procedure;
postcondition "";
precondition "";
}
chain_involvement sa_assessment_delivery "Chain involvement: Assess an alarm" id "a6d5a6be-fa6e-5fa0-a309-08e87ba40c57" {
description "";
source @sa_assessment_procedure;
target @sa_triage;
}
@@ -1,6 +1,4 @@
sa_functional_chain sa_field_procedure "Field repair procedure" id "93eb6479-54e9-59cb-ac7b-16912347bfc0" {
description "";
parent none;
steps [@sa_request_step, @sa_repair_step, @sa_report_step];
}
@@ -8,59 +6,35 @@ sa_occurrence sa_repair_step "Repair the valve" id "0f195fea-3493-5ebd-966d-da9e
body {
Action {
action @sa_repair;
lifeline none;
};
};
description "";
parent @sa_field_procedure;
postcondition "";
precondition "";
}
sa_occurrence sa_report_step "Report the measured result" id "ea32e4ec-8bdc-57ea-8a90-7b09ba4f0eac" {
body {
Exchange {
exchange @sa_repair_report;
message_kind Asynchronous;
receiver none;
reply_to none;
sender none;
values [];
};
};
description "";
parent @sa_field_procedure;
postcondition "";
precondition "";
}
sa_occurrence sa_request_step "Request the field repair" id "40d94322-baa2-5c81-b261-ff47cdc33782" {
body {
Exchange {
exchange @sa_repair_request;
message_kind Asynchronous;
receiver none;
reply_to none;
sender none;
values [];
};
};
description "";
parent @sa_field_procedure;
postcondition "";
precondition "";
}
sequence_link sa_repair_then_report "Sequence link: Report the measured result" id "7fb30b1d-195c-5f0e-a19c-a536f519a899" {
description "";
guard "";
source @sa_repair_step;
target @sa_report_step;
}
sequence_link sa_request_then_repair "Sequence link: Repair the valve" id "6a6d968f-26d6-5dbb-b721-5a27d362779a" {
description "";
guard "";
source @sa_request_step;
target @sa_repair_step;
}
@@ -1,6 +1,4 @@
sa_functional_chain sa_response "Handle a pressure alarm" id "5139ce73-56fd-5875-ba3a-e569fc890231" {
description "";
parent none;
steps [@sa_assess_step, @sa_decision, @sa_reset_step, @sa_use_field, @sa_merge, @sa_confirm_step];
}
@@ -10,23 +8,16 @@ sa_occurrence sa_assess_step "Assess the alarm" id "9024243c-ccc1-5edb-8477-2aa1
sequence @sa_assessment_procedure;
};
};
description "";
parent @sa_response;
postcondition "";
precondition "";
}
sa_occurrence sa_confirm_step "Confirm safe pressure" id "47bec4f9-d6d0-5ed1-adca-fa0d960dc416" {
body {
Action {
action @sa_confirm;
lifeline none;
};
};
description "";
parent @sa_response;
postcondition "";
precondition "";
}
sa_occurrence sa_decision "Can a remote reset safely recover pressure?" id "30c8fad9-a49b-5692-9542-3148e76afd7b" {
@@ -35,10 +26,7 @@ sa_occurrence sa_decision "Can a remote reset safely recover pressure?" id "30c8
kind Choice;
};
};
description "";
parent @sa_response;
postcondition "";
precondition "";
}
sa_occurrence sa_merge "Recovery path completed" id "4ed0dd9d-c9e0-5652-aedf-5f0ab4328a07" {
@@ -47,23 +35,16 @@ sa_occurrence sa_merge "Recovery path completed" id "4ed0dd9d-c9e0-5652-aedf-5f0
kind Merge;
};
};
description "";
parent @sa_response;
postcondition "";
precondition "";
}
sa_occurrence sa_reset_step "Try the authorized remote reset" id "bfa2ef0b-ce68-55c2-a9e5-31ea8a74fce6" {
body {
Action {
action @sa_reset;
lifeline none;
};
};
description "";
parent @sa_response;
postcondition "";
precondition "";
}
sa_occurrence sa_use_field "Use the field repair procedure" id "d5e61c5d-510b-5091-a878-da46f3a9e152" {
@@ -72,56 +53,42 @@ sa_occurrence sa_use_field "Use the field repair procedure" id "d5e61c5d-510b-50
sequence @sa_field_procedure;
};
};
description "";
parent @sa_response;
postcondition "";
precondition "";
}
sequence_link sa_assess_then_decide "Continue" id "9d4a131f-36ad-5125-b21f-c3dd033b8c82" {
description "";
guard "";
source @sa_assess_step;
target @sa_decision;
}
sequence_link sa_field_path "else" id "1bbd5957-1173-51d8-97e3-ba4f084227ae" {
description "";
guard "else";
source @sa_decision;
target @sa_use_field;
}
sequence_link sa_remote_path "remote reset is authorized" id "d8b6710a-e713-5979-934f-e5ca7d155bb4" {
description "";
guard "remote reset is authorized";
source @sa_decision;
target @sa_reset_step;
}
sequence_link sa_confirm_path "Continue" id "1e738f27-a3a1-526d-b330-6384088b9689" {
description "";
guard "";
source @sa_merge;
target @sa_confirm_step;
}
sequence_link sa_remote_merge "Continue" id "e5279265-8eac-52e8-98dc-571b20475e1f" {
description "";
guard "";
source @sa_reset_step;
target @sa_merge;
}
chain_involvement sa_response_delivery "Chain involvement: Restore a safe supply" id "66efc484-53c7-51a3-89e9-422429344b38" {
description "";
source @sa_response;
target @sa_restore;
}
sequence_link sa_field_merge "Continue" id "0f6ac3fb-4ad6-581e-ad14-336472072d5e" {
description "";
guard "";
source @sa_use_field;
target @sa_merge;
}
@@ -1,7 +1,5 @@
sa_scenario sa_conversation "Repair request and measured reply" id "730b27ca-b039-5a3b-8dc6-03418f9061b3" {
context @sa_restore;
description "";
parent none;
postcondition "The controller has received the measured pressure after repair";
precondition "A pressure alarm has been assessed";
steps [@sa_call, @sa_reply];
@@ -14,23 +12,17 @@ sa_occurrence sa_call "Request valve repair" id "8bab4783-a098-5984-9e36-493957c
exchange @sa_contact;
message_kind Call;
receiver @sa_responder;
reply_to none;
sender @sa_caller;
values [{
expression "420.0";
field @sa_pressure_parameter;
path [];
}];
};
};
description "";
parent @sa_conversation;
postcondition "";
precondition "";
}
sa_lifeline sa_caller "Controller" id "98c61788-2fee-51f4-8cf9-de48e8c70e9e" {
description "";
parent @sa_conversation;
represented @sa_control;
}
@@ -46,18 +38,13 @@ sa_occurrence sa_reply "Return pressure after repair" id "4e1c0a56-ce80-5f13-a93
values [{
expression "320.0";
field @sa_pressure_parameter;
path [];
}];
};
};
description "";
parent @sa_conversation;
postcondition "";
precondition "";
}
sa_lifeline sa_responder "Technician" id "6249b54a-e396-5338-82b9-89266c55670d" {
description "";
parent @sa_conversation;
represented @sa_technician;
}
@@ -1,7 +1,5 @@
sa_scenario sa_full_scenario "Restore supply through field repair" id "f746a1cd-ff9f-55c0-b325-01c03d2f4fae" {
context @sa_restore;
description "";
parent none;
postcondition "The controller has received the measured pressure after repair";
precondition "A pressure alarm has been assessed";
steps [@sa_use_conversation];
@@ -9,13 +7,11 @@ sa_scenario sa_full_scenario "Restore supply through field repair" id "f746a1cd-
}
sa_lifeline sa_outer_controller "Controller" id "1edfcf9e-9bfa-51f1-80d5-62b8fd8bb023" {
description "";
parent @sa_full_scenario;
represented @sa_control;
}
sa_lifeline sa_outer_technician "Technician" id "155e4fe4-9e3b-5f4e-b886-28abcc3f086b" {
description "";
parent @sa_full_scenario;
represented @sa_technician;
}
@@ -33,9 +29,6 @@ sa_occurrence sa_use_conversation "Use the reviewed repair conversation" id "1c9
scenario @sa_conversation;
};
};
description "";
parent @sa_full_scenario;
postcondition "";
precondition "";
}
@@ -1,80 +1,43 @@
sa_state_machine sa_monitor "Pressure supervision" id "132b3f28-3f88-5327-9868-8c8b645ed412" {
description "";
parent @sa_control;
}
sa_region sa_monitor_region "Supervision cycle" id "3ab11e24-4140-58fc-b3fa-da5f51c7fe2c" {
description "";
parent @sa_monitor;
}
sa_initial sa_monitor_initial "Start" id "57eb5348-14d8-560e-996a-8dbf993243e2" {
description "";
do_activity "";
entry "";
exit "";
kind Initial;
parent @sa_monitor_region;
}
sa_state sa_monitoring "Monitoring" id "8af21f9b-c21e-5aae-af0d-0958840f6655" {
description "";
do_activity "";
entry "";
exit "";
kind State;
parent @sa_monitor_region;
}
sa_state sa_waiting "Awaiting repair" id "a6bb10b1-f1e5-5b31-8afd-5bad5a7b6c37" {
description "";
do_activity "";
entry "";
exit "";
kind State;
parent @sa_monitor_region;
}
state_transition sa_start_monitoring "State transition: Monitoring" id "47730532-0372-5cc5-9d6d-0b8953aebdd8" {
description "";
effect "";
effects [];
guard "";
source @sa_monitor_initial;
target @sa_monitoring;
transition_kind External;
trigger "";
triggers [];
}
state_transition sa_alarm_transition "State transition: Awaiting repair" id "13c3bfa4-9254-5ab2-b96f-f65f402adf81" {
description "";
effect "";
effects [];
guard "";
source @sa_monitoring;
target @sa_waiting;
transition_kind External;
trigger "";
triggers [@sa_high_pressure];
}
state_action sa_assess_on_entry "Assess when repair is needed" id "a887f96a-37cb-5891-8070-011fa276e5b5" {
description "";
phase Entry;
source @sa_waiting;
target @sa_assess;
}
state_transition sa_normal_transition "State transition: Monitoring" id "6e2c1b47-764c-577a-b325-ff66dd2a2768" {
description "";
effect "";
effects [];
guard "repair confirmed and pressure is safe";
source @sa_waiting;
target @sa_monitoring;
transition_kind External;
trigger "";
triggers [];
}
@@ -1,26 +1,18 @@
sa_mission sa_mission "Maintain safe water supply" id "5ad034b1-084d-5273-9e70-971671766132" {
description "";
parent none;
}
sa_capability sa_restore "Restore a safe supply" id "81259051-370a-512d-a3b5-6420a6f933b6" {
description "";
parent none;
}
sa_capability sa_triage "Assess an alarm" id "bad8fa7c-f634-55fd-98d3-e1eb2da05d8f" {
description "";
parent none;
}
mission_capability sa_mission_delivery "Mission capability: Restore a safe supply" id "a0902e0f-3d18-576a-9aca-975a136dfdd7" {
description "";
source @sa_mission;
target @sa_restore;
}
capability_include sa_include_triage "Capability include: Assess an alarm" id "f3fe7690-734a-5877-8b57-a6599a4033c4" {
description "";
source @sa_restore;
target @sa_triage;
}
@@ -1,5 +1,4 @@
sa_function sa_audit_thresholds "Audit pressure thresholds" id "4149a872-3440-59de-9879-8981e78d27ab" {
description "Deliberately unfinished: assigned to the service, but no behavior context yet. Model checks should expose this one work_without_behavior_context warning.";
parent none;
}
@@ -1,10 +1,7 @@
sa_function sa_restore_work "Restore safe pressure" id "58867e7d-916e-5ebc-9e4c-8dbed1314be3" {
description "";
parent none;
}
sa_function sa_assess "Assess pressure alarm" id "dddae020-a444-5d48-acc8-139d2696a672" {
description "";
parent @sa_restore_work;
}
@@ -14,13 +11,10 @@ sa_port sa_repair_request_out "Request field repair output" id "24a6850e-b819-57
outgoing_items [@sa_repair_operation];
};
};
description "";
legacy none;
parent @sa_assess;
}
sa_function sa_confirm "Confirm safe pressure" id "d75eb3fa-a27d-52d5-8782-8b342b3865f6" {
description "";
parent @sa_restore_work;
}
@@ -30,13 +24,10 @@ sa_port sa_repair_report_in "Report repair result input" id "391af7c4-b07d-53a7-
incoming_items [@sa_repair_operation];
};
};
description "";
legacy none;
parent @sa_confirm;
}
sa_function sa_repair "Repair valve in the field" id "9497e10f-04c4-5586-8738-2464a6f00bd4" {
description "";
parent @sa_restore_work;
}
@@ -46,8 +37,6 @@ sa_port sa_repair_report_out "Report repair result output" id "994faed7-dbc8-5b9
outgoing_items [@sa_repair_operation];
};
};
description "";
legacy none;
parent @sa_repair;
}
@@ -57,24 +46,19 @@ sa_port sa_repair_request_in "Request field repair input" id "d2113fb0-a531-54ef
incoming_items [@sa_repair_operation];
};
};
description "";
legacy none;
parent @sa_repair;
}
sa_function sa_reset "Reset valve remotely" id "09342ccb-044b-5483-ac58-87dc3cf7831c" {
description "";
parent @sa_restore_work;
}
capability_involvement sa_assessment_support "Capability involvement: Assess an alarm" id "93eaad8d-83b7-5980-b9bb-86c9e3f31da4" {
description "";
source @sa_assess;
target @sa_triage;
}
functional_exchange sa_repair_request "Request field repair" id "93474111-f23e-56c1-b61b-b7c2396b91bd" {
description "";
items [@sa_repair_operation];
source @sa_assess;
source_port @sa_repair_request_out;
@@ -83,7 +67,6 @@ functional_exchange sa_repair_request "Request field repair" id "93474111-f23e-5
}
functional_exchange sa_repair_report "Report repair result" id "684eadc3-e670-5779-a60f-bbc9a5bfb062" {
description "";
items [@sa_repair_operation];
source @sa_repair;
source_port @sa_repair_report_out;
-7
View File
@@ -1,8 +1,6 @@
sa_exchange_category sa_contact_category "Field contact" id "781cbe75-410f-5cc5-aeb8-a43491f77b99" {
description "";
exchanges [@sa_contact];
family "Participant";
parent none;
}
sa_event sa_high_pressure "Pressure alarm detected" id "01490fa8-2f01-5ac8-9722-d55d45414379" {
@@ -11,14 +9,9 @@ sa_event sa_high_pressure "Pressure alarm detected" id "01490fa8-2f01-5ac8-9722-
expression "measured_pressure > operating_limit";
};
};
description "";
parent none;
}
sa_exchange_category sa_repair_traffic "Repair work exchanges" id "a9a47e48-100c-57a8-95f0-7fe24da94f94" {
description "";
exchanges [@sa_repair_request, @sa_repair_report];
family "Work";
parent none;
}
-21
View File
@@ -1,6 +1,4 @@
sa_system sa_control "Pressure coordination service" id "435bf87c-73fd-5e4d-978a-4c471bc9eaf0" {
description "";
parent none;
}
sa_port sa_control_port "Repair service client" id "98d411e7-7c65-5c3f-b543-8bf0f2522ea2" {
@@ -11,13 +9,10 @@ sa_port sa_control_port "Repair service client" id "98d411e7-7c65-5c3f-b543-8bf0
};
};
description "Explicitly chosen Standard component port for the repair service.";
legacy none;
parent @sa_control;
}
sa_actor sa_technician "Field technician" id "c05b9422-b9da-5de5-b21d-07ad23bd2b8a" {
description "";
parent none;
}
sa_port sa_technician_port "Repair service provider" id "e6d69cbd-99d7-596b-bbe3-dada6118a600" {
@@ -28,36 +23,30 @@ sa_port sa_technician_port "Repair service provider" id "e6d69cbd-99d7-596b-bbe3
};
};
description "Explicitly chosen Standard component port for the repair service.";
legacy none;
parent @sa_technician;
}
allocation sa_assess_allocation "Allocation: Assess pressure alarm" id "9b9356d7-2db2-5a94-a47e-ea892cf02afe" {
description "";
source @sa_control;
target @sa_assess;
}
allocation sa_audit_allocation "Allocation: Audit pressure thresholds" id "1b245065-7242-5d81-8472-aaeb932054c9" {
description "";
source @sa_control;
target @sa_audit_thresholds;
}
allocation sa_confirm_allocation "Allocation: Confirm safe pressure" id "b76bf8de-2fa4-563f-a540-dd30f30e6c49" {
description "";
source @sa_control;
target @sa_confirm;
}
interface_requirement sa_client_requires "Interface requirement: Valve repair service" id "4f85337b-f6eb-533c-a45a-9a5d1d9567ca" {
description "";
source @sa_control_port;
target @sa_repair_interface;
}
boundary_exchange sa_contact "Technician service conversation" id "63009b5f-539e-5914-b630-ec6b7fefb4d5" {
description "";
items [@sa_repair_operation];
oriented false;
source @sa_control_port;
@@ -65,61 +54,51 @@ boundary_exchange sa_contact "Technician service conversation" id "63009b5f-539e
}
exchange_allocation sa_report_carriage "Exchange allocation: Report repair result" id "ff9897b3-a23a-5994-a5b3-99a5b4d34ceb" {
description "";
source @sa_contact;
target @sa_repair_report;
}
exchange_allocation sa_request_carriage "Exchange allocation: Request field repair" id "4c4c4851-5a3d-5bb9-94df-b19f9cf39819" {
description "";
source @sa_contact;
target @sa_repair_request;
}
port_allocation sa_port_report_in "Port allocation: Report repair result input" id "568c5c01-de21-52a2-83bd-afdddc158085" {
description "";
source @sa_control_port;
target @sa_repair_report_in;
}
port_allocation sa_port_request_out "Port allocation: Request field repair output" id "55ec4dbf-bd17-5b0c-b3a3-95b2194aa8b9" {
description "";
source @sa_control_port;
target @sa_repair_request_out;
}
mission_involvement sa_mission_participant "Mission involvement: Maintain safe water supply" id "1c1e5634-ba8c-591f-b143-90a27cdc6cd2" {
description "";
source @sa_control;
target @sa_mission;
}
allocation sa_reset_allocation "Allocation: Reset valve remotely" id "3cc9ab52-2b00-57fd-8709-aabc75e5d3e3" {
description "";
source @sa_control;
target @sa_reset;
}
allocation sa_repair_allocation "Allocation: Repair valve in the field" id "725f2e1b-aa2c-5615-9b1f-28da178168fa" {
description "";
source @sa_technician;
target @sa_repair;
}
port_allocation sa_port_report_out "Port allocation: Report repair result output" id "e53b7a40-e8a3-5b3f-826d-c2b5c164a84a" {
description "";
source @sa_technician_port;
target @sa_repair_report_out;
}
port_allocation sa_port_request_in "Port allocation: Request field repair input" id "09eaa992-4cdf-5ea3-b748-f10fd74b8cba" {
description "";
source @sa_technician_port;
target @sa_repair_request_in;
}
interface_provision sa_provider_provides "Interface provision: Valve repair service" id "55f89fbf-aa3c-5ae1-95ef-ac676f9c1ada" {
description "";
source @sa_technician_port;
target @sa_repair_interface;
}
-18
View File
@@ -1,48 +1,30 @@
sa_data_type sa_pressure "Measured pressure" id "87d0a95b-af1b-5352-b753-e4c6533671a5" {
default none;
definition {
Real {
maximum 600.0;
minimum 0.0;
};
};
description "";
parent none;
unit "kPa";
}
sa_interface sa_repair_interface "Valve repair service" id "a4b8942e-a975-5f88-ad14-8f38c345baac" {
description "";
parent none;
supertype none;
}
sa_exchange_item sa_repair_operation "Valve repair" id "a962fc5e-3ab0-5296-8b0d-fd84633f9218" {
description "";
kind Information;
mechanism Operation;
parent none;
supertype none;
}
sa_data_field sa_pressure_parameter "Pressure before / after repair" id "bb12ba5d-aab5-5a1b-99de-c767f5365b57" {
default none;
description "";
direction InOut;
maximum 1;
minimum 1;
parent @sa_repair_operation;
storage Value;
type_ref {
Named @sa_pressure;
};
unit "";
}
interface_item_allocation sa_repair_interface_item "Repair operation contract" id "f074e041-23cf-54ad-af9c-f171e30621e7" {
acquisition Accept;
description "";
order 0;
source @sa_repair_interface;
target @sa_repair_operation;
transmission Synchronous;
-73
View File
@@ -9,7 +9,6 @@ trace trace_oa_alarm_transition "State transition: Awaiting repair ↔ State tra
name "State transition: Awaiting repair";
};
};
description "";
kind Manual;
oa @oa_alarm_transition;
sa @sa_alarm_transition;
@@ -26,8 +25,6 @@ trace trace_oa_assess "Assess pressure alarm ↔ Assess pressure alarm" id "5309
name "Assess pressure alarm";
};
};
description "";
kind Refinement;
oa @oa_assess;
sa @sa_assess;
}
@@ -43,7 +40,6 @@ trace trace_oa_assess_allocation "Allocation: Assess pressure alarm ↔ Allocati
name "Allocation: Assess pressure alarm";
};
};
description "";
kind Manual;
oa @oa_assess_allocation;
sa @sa_assess_allocation;
@@ -60,7 +56,6 @@ trace trace_oa_assess_on_entry "Assess when repair is needed ↔ Assess when rep
name "Assess when repair is needed";
};
};
description "";
kind Manual;
oa @oa_assess_on_entry;
sa @sa_assess_on_entry;
@@ -77,7 +72,6 @@ trace trace_oa_assess_step "Assess the alarm ↔ Assess the alarm" id "e848da41-
name "Assess the alarm";
};
};
description "";
kind Sequence;
oa @oa_assess_step;
sa @sa_assess_step;
@@ -94,7 +88,6 @@ trace trace_oa_assess_then_decide "Continue ↔ Continue" id "a5c1eb01-a95f-5f7a
name "Continue";
};
};
description "";
kind Manual;
oa @oa_assess_then_decide;
sa @sa_assess_then_decide;
@@ -111,7 +104,6 @@ trace trace_oa_assessment_action "Assess pressure alarm ↔ Assess pressure alar
name "Assess pressure alarm";
};
};
description "";
kind Sequence;
oa @oa_assessment_action;
sa @sa_assessment_action;
@@ -128,7 +120,6 @@ trace trace_oa_assessment_delivery "Chain involvement: Assess an alarm ↔ Chain
name "Chain involvement: Assess an alarm";
};
};
description "";
kind Manual;
oa @oa_assessment_delivery;
sa @sa_assessment_delivery;
@@ -145,7 +136,6 @@ trace trace_oa_assessment_procedure "Assess an alarm procedure ↔ Assess an ala
name "Assess an alarm procedure";
};
};
description "";
kind Sequence;
oa @oa_assessment_procedure;
sa @sa_assessment_procedure;
@@ -162,7 +152,6 @@ trace trace_oa_assessment_support "Capability involvement: Assess an alarm ↔ C
name "Capability involvement: Assess an alarm";
};
};
description "";
kind Manual;
oa @oa_assessment_support;
sa @sa_assessment_support;
@@ -179,7 +168,6 @@ trace trace_oa_call "Request valve repair ↔ Request valve repair" id "f0cc86e5
name "Request valve repair";
};
};
description "";
kind Scenario;
oa @oa_call;
sa @sa_call;
@@ -196,7 +184,6 @@ trace trace_oa_caller "Controller ↔ Controller" id "241dfdac-2450-5704-a4cb-9f
name "Controller";
};
};
description "";
kind Scenario;
oa @oa_caller;
sa @sa_caller;
@@ -213,8 +200,6 @@ trace trace_oa_confirm "Confirm safe pressure ↔ Confirm safe pressure" id "d77
name "Confirm safe pressure";
};
};
description "";
kind Refinement;
oa @oa_confirm;
sa @sa_confirm;
}
@@ -230,7 +215,6 @@ trace trace_oa_confirm_allocation "Allocation: Confirm safe pressure ↔ Allocat
name "Allocation: Confirm safe pressure";
};
};
description "";
kind Manual;
oa @oa_confirm_allocation;
sa @sa_confirm_allocation;
@@ -247,7 +231,6 @@ trace trace_oa_confirm_path "Continue ↔ Continue" id "a106d183-c239-5c9f-b1f1-
name "Continue";
};
};
description "";
kind Manual;
oa @oa_confirm_path;
sa @sa_confirm_path;
@@ -264,7 +247,6 @@ trace trace_oa_confirm_step "Confirm safe pressure ↔ Confirm safe pressure" id
name "Confirm safe pressure";
};
};
description "";
kind Sequence;
oa @oa_confirm_step;
sa @sa_confirm_step;
@@ -281,7 +263,6 @@ trace trace_oa_contact "Technician service conversation ↔ Technician service c
name "Technician service conversation";
};
};
description "";
kind Exchange;
oa @oa_contact;
sa @sa_contact;
@@ -298,7 +279,6 @@ trace trace_oa_contact_category "Field contact ↔ Field contact" id "e96ff119-b
name "Field contact";
};
};
description "";
kind Manual;
oa @oa_contact_category;
sa @sa_contact_category;
@@ -315,7 +295,6 @@ trace trace_oa_control "Control room ↔ Pressure coordination service" id "aba5
name "Pressure coordination service";
};
};
description "";
kind Participant;
oa @oa_control;
sa @sa_control;
@@ -332,7 +311,6 @@ trace trace_oa_conversation "Repair request and measured reply ↔ Repair reques
name "Repair request and measured reply";
};
};
description "";
kind Scenario;
oa @oa_conversation;
sa @sa_conversation;
@@ -349,7 +327,6 @@ trace trace_oa_decision "Can a remote reset safely recover pressure? ↔ Can a r
name "Can a remote reset safely recover pressure?";
};
};
description "";
kind Sequence;
oa @oa_decision;
sa @sa_decision;
@@ -366,7 +343,6 @@ trace trace_oa_field_merge "Continue ↔ Continue" id "d32dd433-9c9e-5b57-9355-2
name "Continue";
};
};
description "";
kind Manual;
oa @oa_field_merge;
sa @sa_field_merge;
@@ -383,7 +359,6 @@ trace trace_oa_field_path "else ↔ else" id "2e849e31-35aa-5aef-a989-26948038e5
name "else";
};
};
description "";
kind Manual;
oa @oa_field_path;
sa @sa_field_path;
@@ -400,7 +375,6 @@ trace trace_oa_field_procedure "Field repair procedure ↔ Field repair procedur
name "Field repair procedure";
};
};
description "";
kind Sequence;
oa @oa_field_procedure;
sa @sa_field_procedure;
@@ -417,7 +391,6 @@ trace trace_oa_full_scenario "Restore supply through field repair ↔ Restore su
name "Restore supply through field repair";
};
};
description "";
kind Scenario;
oa @oa_full_scenario;
sa @sa_full_scenario;
@@ -434,7 +407,6 @@ trace trace_oa_high_pressure "Pressure alarm detected ↔ Pressure alarm detecte
name "Pressure alarm detected";
};
};
description "";
kind State;
oa @oa_high_pressure;
sa @sa_high_pressure;
@@ -451,7 +423,6 @@ trace trace_oa_include_triage "Capability include: Assess an alarm ↔ Capabilit
name "Capability include: Assess an alarm";
};
};
description "";
kind Manual;
oa @oa_include_triage;
sa @sa_include_triage;
@@ -468,7 +439,6 @@ trace trace_oa_merge "Recovery path completed ↔ Recovery path completed" id "0
name "Recovery path completed";
};
};
description "";
kind Sequence;
oa @oa_merge;
sa @sa_merge;
@@ -485,7 +455,6 @@ trace trace_oa_mission "Maintain safe water supply ↔ Maintain safe water suppl
name "Maintain safe water supply";
};
};
description "";
kind Mission;
oa @oa_mission;
sa @sa_mission;
@@ -502,7 +471,6 @@ trace trace_oa_mission_delivery "Mission capability: Restore a safe supply ↔ M
name "Mission capability: Restore a safe supply";
};
};
description "";
kind Manual;
oa @oa_mission_delivery;
sa @sa_mission_delivery;
@@ -519,7 +487,6 @@ trace trace_oa_mission_participant "Mission involvement: Maintain safe water sup
name "Mission involvement: Maintain safe water supply";
};
};
description "";
kind Manual;
oa @oa_mission_participant;
sa @sa_mission_participant;
@@ -536,7 +503,6 @@ trace trace_oa_monitor "Pressure supervision ↔ Pressure supervision" id "b5386
name "Pressure supervision";
};
};
description "";
kind State;
oa @oa_monitor;
sa @sa_monitor;
@@ -553,7 +519,6 @@ trace trace_oa_monitor_initial "Start ↔ Start" id "48d243f0-553f-507e-acb0-947
name "Start";
};
};
description "";
kind State;
oa @oa_monitor_initial;
sa @sa_monitor_initial;
@@ -570,7 +535,6 @@ trace trace_oa_monitor_region "Supervision cycle ↔ Supervision cycle" id "2087
name "Supervision cycle";
};
};
description "";
kind State;
oa @oa_monitor_region;
sa @sa_monitor_region;
@@ -587,7 +551,6 @@ trace trace_oa_monitoring "Monitoring ↔ Monitoring" id "12ec320f-2e14-5a5a-8c6
name "Monitoring";
};
};
description "";
kind State;
oa @oa_monitoring;
sa @sa_monitoring;
@@ -604,7 +567,6 @@ trace trace_oa_normal_transition "State transition: Monitoring ↔ State transit
name "State transition: Monitoring";
};
};
description "";
kind Manual;
oa @oa_normal_transition;
sa @sa_normal_transition;
@@ -621,7 +583,6 @@ trace trace_oa_outer_controller "Controller ↔ Controller" id "fd195498-dbba-52
name "Controller";
};
};
description "";
kind Scenario;
oa @oa_outer_controller;
sa @sa_outer_controller;
@@ -638,7 +599,6 @@ trace trace_oa_outer_technician "Technician ↔ Technician" id "c132a0df-1270-55
name "Technician";
};
};
description "";
kind Scenario;
oa @oa_outer_technician;
sa @sa_outer_technician;
@@ -655,7 +615,6 @@ trace trace_oa_pressure "Measured pressure ↔ Measured pressure" id "d9d47371-e
name "Measured pressure";
};
};
description "";
kind Item;
oa @oa_pressure;
sa @sa_pressure;
@@ -672,7 +631,6 @@ trace trace_oa_pressure_parameter "Pressure before / after repair ↔ Pressure b
name "Pressure before / after repair";
};
};
description "";
kind Item;
oa @oa_pressure_parameter;
sa @sa_pressure_parameter;
@@ -689,7 +647,6 @@ trace trace_oa_remote_merge "Continue ↔ Continue" id "1b788e74-36b2-5c17-a936-
name "Continue";
};
};
description "";
kind Manual;
oa @oa_remote_merge;
sa @sa_remote_merge;
@@ -706,7 +663,6 @@ trace trace_oa_remote_path "remote reset is authorized ↔ remote reset is autho
name "remote reset is authorized";
};
};
description "";
kind Manual;
oa @oa_remote_path;
sa @sa_remote_path;
@@ -723,8 +679,6 @@ trace trace_oa_repair "Repair valve in the field ↔ Repair valve in the field"
name "Repair valve in the field";
};
};
description "";
kind Refinement;
oa @oa_repair;
sa @sa_repair;
}
@@ -740,7 +694,6 @@ trace trace_oa_repair_allocation "Allocation: Repair valve in the field ↔ Allo
name "Allocation: Repair valve in the field";
};
};
description "";
kind Manual;
oa @oa_repair_allocation;
sa @sa_repair_allocation;
@@ -757,7 +710,6 @@ trace trace_oa_repair_operation "Valve repair ↔ Valve repair" id "cf92cedf-ea1
name "Valve repair";
};
};
description "";
kind Item;
oa @oa_repair_operation;
sa @sa_repair_operation;
@@ -774,7 +726,6 @@ trace trace_oa_repair_report "Report repair result ↔ Report repair result" id
name "Report repair result";
};
};
description "";
kind Exchange;
oa @oa_repair_report;
sa @sa_repair_report;
@@ -791,7 +742,6 @@ trace trace_oa_repair_request "Request field repair ↔ Request field repair" id
name "Request field repair";
};
};
description "";
kind Exchange;
oa @oa_repair_request;
sa @sa_repair_request;
@@ -808,7 +758,6 @@ trace trace_oa_repair_step "Repair the valve ↔ Repair the valve" id "ac3aa6b0-
name "Repair the valve";
};
};
description "";
kind Sequence;
oa @oa_repair_step;
sa @sa_repair_step;
@@ -825,7 +774,6 @@ trace trace_oa_repair_then_report "Sequence link: Report the measured result ↔
name "Sequence link: Report the measured result";
};
};
description "";
kind Manual;
oa @oa_repair_then_report;
sa @sa_repair_then_report;
@@ -842,7 +790,6 @@ trace trace_oa_repair_traffic "Repair work exchanges ↔ Repair work exchanges"
name "Repair work exchanges";
};
};
description "";
kind Manual;
oa @oa_repair_traffic;
sa @sa_repair_traffic;
@@ -859,7 +806,6 @@ trace trace_oa_reply "Return pressure after repair ↔ Return pressure after rep
name "Return pressure after repair";
};
};
description "";
kind Scenario;
oa @oa_reply;
sa @sa_reply;
@@ -876,7 +822,6 @@ trace trace_oa_report_step "Report the measured result ↔ Report the measured r
name "Report the measured result";
};
};
description "";
kind Sequence;
oa @oa_report_step;
sa @sa_report_step;
@@ -893,7 +838,6 @@ trace trace_oa_request_step "Request the field repair ↔ Request the field repa
name "Request the field repair";
};
};
description "";
kind Sequence;
oa @oa_request_step;
sa @sa_request_step;
@@ -910,7 +854,6 @@ trace trace_oa_request_then_repair "Sequence link: Repair the valve ↔ Sequence
name "Sequence link: Repair the valve";
};
};
description "";
kind Manual;
oa @oa_request_then_repair;
sa @sa_request_then_repair;
@@ -927,8 +870,6 @@ trace trace_oa_reset "Reset valve remotely ↔ Reset valve remotely" id "82a5be1
name "Reset valve remotely";
};
};
description "";
kind Refinement;
oa @oa_reset;
sa @sa_reset;
}
@@ -944,7 +885,6 @@ trace trace_oa_reset_allocation "Allocation: Reset valve remotely ↔ Allocation
name "Allocation: Reset valve remotely";
};
};
description "";
kind Manual;
oa @oa_reset_allocation;
sa @sa_reset_allocation;
@@ -961,7 +901,6 @@ trace trace_oa_reset_step "Try the authorized remote reset ↔ Try the authorize
name "Try the authorized remote reset";
};
};
description "";
kind Sequence;
oa @oa_reset_step;
sa @sa_reset_step;
@@ -978,7 +917,6 @@ trace trace_oa_responder "Technician ↔ Technician" id "c5051628-b7af-5fb1-a014
name "Technician";
};
};
description "";
kind Scenario;
oa @oa_responder;
sa @sa_responder;
@@ -995,7 +933,6 @@ trace trace_oa_response "Handle a pressure alarm ↔ Handle a pressure alarm" id
name "Handle a pressure alarm";
};
};
description "";
kind Sequence;
oa @oa_response;
sa @sa_response;
@@ -1012,7 +949,6 @@ trace trace_oa_response_delivery "Chain involvement: Restore a safe supply ↔ C
name "Chain involvement: Restore a safe supply";
};
};
description "";
kind Manual;
oa @oa_response_delivery;
sa @sa_response_delivery;
@@ -1029,7 +965,6 @@ trace trace_oa_restore "Restore a safe supply ↔ Restore a safe supply" id "0fb
name "Restore a safe supply";
};
};
description "";
kind Capability;
oa @oa_restore;
sa @sa_restore;
@@ -1046,8 +981,6 @@ trace trace_oa_restore_work "Restore safe pressure ↔ Restore safe pressure" id
name "Restore safe pressure";
};
};
description "";
kind Refinement;
oa @oa_restore_work;
sa @sa_restore_work;
}
@@ -1063,7 +996,6 @@ trace trace_oa_start_monitoring "State transition: Monitoring ↔ State transiti
name "State transition: Monitoring";
};
};
description "";
kind Manual;
oa @oa_start_monitoring;
sa @sa_start_monitoring;
@@ -1080,7 +1012,6 @@ trace trace_oa_technician "Field technician ↔ Field technician" id "d47d3c36-5
name "Field technician";
};
};
description "";
kind Participant;
oa @oa_technician;
sa @sa_technician;
@@ -1097,7 +1028,6 @@ trace trace_oa_triage "Assess an alarm ↔ Assess an alarm" id "6acd072d-431e-58
name "Assess an alarm";
};
};
description "";
kind Capability;
oa @oa_triage;
sa @sa_triage;
@@ -1114,7 +1044,6 @@ trace trace_oa_use_conversation "Use the reviewed repair conversation ↔ Use th
name "Use the reviewed repair conversation";
};
};
description "";
kind Scenario;
oa @oa_use_conversation;
sa @sa_use_conversation;
@@ -1131,7 +1060,6 @@ trace trace_oa_use_field "Use the field repair procedure ↔ Use the field repai
name "Use the field repair procedure";
};
};
description "";
kind Sequence;
oa @oa_use_field;
sa @sa_use_field;
@@ -1148,7 +1076,6 @@ trace trace_oa_waiting "Awaiting repair ↔ Awaiting repair" id "0754be23-e4b8-5
name "Awaiting repair";
};
};
description "";
kind State;
oa @oa_waiting;
sa @sa_waiting;
@@ -1,21 +1,9 @@
diagram oa_allocation_view "1 · OA responsibility and repair traffic" id "103ea62c-ac56-5827-94e7-cf09bca96ccc" {
description "Teaching view: positions and filters do not change ownership or exchange identities.";
layer Oa;
projection Allocation;
routes [];
view_options {
coverage "All";
exchange_labels "Name";
hidden_edges [];
participant_categories "All";
relation_kinds "All";
work_categories "All";
work_contexts "All";
};
}
node oa_allocation_view_oa_assess id "5ae1c46d-59a3-50fd-8cc3-b292df21f951" {
collapsed false;
diagram @oa_allocation_view;
element @oa_assess;
position {
@@ -29,7 +17,6 @@ node oa_allocation_view_oa_assess id "5ae1c46d-59a3-50fd-8cc3-b292df21f951" {
}
node oa_allocation_view_oa_confirm id "edd74060-ac61-5450-a57c-a6577daa5d50" {
collapsed false;
diagram @oa_allocation_view;
element @oa_confirm;
position {
@@ -43,7 +30,6 @@ node oa_allocation_view_oa_confirm id "edd74060-ac61-5450-a57c-a6577daa5d50" {
}
node oa_allocation_view_oa_control id "3191e05c-1c7f-5f0e-beb0-5f3bded54c48" {
collapsed false;
diagram @oa_allocation_view;
element @oa_control;
position {
@@ -57,7 +43,6 @@ node oa_allocation_view_oa_control id "3191e05c-1c7f-5f0e-beb0-5f3bded54c48" {
}
node oa_allocation_view_oa_repair id "202ebe37-ddb4-5099-8ce9-ec3e895ab9e9" {
collapsed false;
diagram @oa_allocation_view;
element @oa_repair;
position {
@@ -71,7 +56,6 @@ node oa_allocation_view_oa_repair id "202ebe37-ddb4-5099-8ce9-ec3e895ab9e9" {
}
node oa_allocation_view_oa_reset id "7066f78c-9736-5349-b94a-0587331b980e" {
collapsed false;
diagram @oa_allocation_view;
element @oa_reset;
position {
@@ -85,7 +69,6 @@ node oa_allocation_view_oa_reset id "7066f78c-9736-5349-b94a-0587331b980e" {
}
node oa_allocation_view_oa_technician id "d27f28ef-f10a-5355-9a05-6f2fd39b7de6" {
collapsed false;
diagram @oa_allocation_view;
element @oa_technician;
position {
@@ -2,20 +2,9 @@ diagram sa_allocation_view "2 · SA service boundary and functions" id "35d3c24b
description "Teaching view: positions and filters do not change ownership or exchange identities.";
layer Sa;
projection Allocation;
routes [];
view_options {
coverage "All";
exchange_labels "Name";
hidden_edges [];
participant_categories "All";
relation_kinds "All";
work_categories "All";
work_contexts "All";
};
}
node sa_allocation_view_sa_assess id "60f6ad74-b69e-5aef-b832-f1bd60b903ae" {
collapsed false;
diagram @sa_allocation_view;
element @sa_assess;
position {
@@ -29,7 +18,6 @@ node sa_allocation_view_sa_assess id "60f6ad74-b69e-5aef-b832-f1bd60b903ae" {
}
node sa_allocation_view_sa_confirm id "7e3ba36f-bc68-5520-bdb9-77083bb7bba2" {
collapsed false;
diagram @sa_allocation_view;
element @sa_confirm;
position {
@@ -43,7 +31,6 @@ node sa_allocation_view_sa_confirm id "7e3ba36f-bc68-5520-bdb9-77083bb7bba2" {
}
node sa_allocation_view_sa_control id "8345c76a-8d85-5c31-902e-ec1cd33ed4a3" {
collapsed false;
diagram @sa_allocation_view;
element @sa_control;
position {
@@ -57,7 +44,6 @@ node sa_allocation_view_sa_control id "8345c76a-8d85-5c31-902e-ec1cd33ed4a3" {
}
node sa_allocation_view_sa_repair id "34630a18-41b0-5fdd-bff4-ee86e93e8651" {
collapsed false;
diagram @sa_allocation_view;
element @sa_repair;
position {
@@ -71,7 +57,6 @@ node sa_allocation_view_sa_repair id "34630a18-41b0-5fdd-bff4-ee86e93e8651" {
}
node sa_allocation_view_sa_reset id "73406244-ebd7-5995-86c7-e17c73c50b32" {
collapsed false;
diagram @sa_allocation_view;
element @sa_reset;
position {
@@ -85,7 +70,6 @@ node sa_allocation_view_sa_reset id "73406244-ebd7-5995-86c7-e17c73c50b32" {
}
node sa_allocation_view_sa_technician id "f7225d1c-e506-505d-bb20-916950269d4b" {
collapsed false;
diagram @sa_allocation_view;
element @sa_technician;
position {
@@ -2,20 +2,9 @@ diagram sa_capability_view "4 · SA mission and included capability" id "3e6ad19
description "Teaching view: positions and filters do not change ownership or exchange identities.";
layer Sa;
projection Capabilities;
routes [];
view_options {
coverage "All";
exchange_labels "Name";
hidden_edges [];
participant_categories "All";
relation_kinds "All";
work_categories "All";
work_contexts "All";
};
}
node sa_capability_view_sa_control id "41ddb68b-bd2b-5f91-82eb-efde01157828" {
collapsed false;
diagram @sa_capability_view;
element @sa_control;
position {
@@ -29,7 +18,6 @@ node sa_capability_view_sa_control id "41ddb68b-bd2b-5f91-82eb-efde01157828" {
}
node sa_capability_view_sa_mission id "4b814335-bf98-5b68-85d5-ea549766517a" {
collapsed false;
diagram @sa_capability_view;
element @sa_mission;
position {
@@ -43,7 +31,6 @@ node sa_capability_view_sa_mission id "4b814335-bf98-5b68-85d5-ea549766517a" {
}
node sa_capability_view_sa_restore id "d3bc13f9-7726-5280-83fe-63f391c45afb" {
collapsed false;
diagram @sa_capability_view;
element @sa_restore;
position {
@@ -57,7 +44,6 @@ node sa_capability_view_sa_restore id "d3bc13f9-7726-5280-83fe-63f391c45afb" {
}
node sa_capability_view_sa_triage id "069a3fb3-077c-59d8-91da-27b0665143c7" {
collapsed false;
diagram @sa_capability_view;
element @sa_triage;
position {
@@ -2,24 +2,18 @@ diagram sa_service_view "3 · SA service conversation only" id "50af4c9b-93ce-5f
description "Teaching view: positions and filters do not change ownership or exchange identities.";
layer Sa;
projection Allocation;
routes [];
view_options {
coverage "All";
exchange_labels "NameAndItems";
hidden_edges [];
participant_categories {
Any [@sa_contact_category];
};
relation_kinds "All";
work_categories {
Any [];
};
work_contexts "All";
};
}
node sa_service_view_sa_assess id "439f1e45-e5ed-567c-978b-4c93ef267db1" {
collapsed false;
diagram @sa_service_view;
element @sa_assess;
position {
@@ -33,7 +27,6 @@ node sa_service_view_sa_assess id "439f1e45-e5ed-567c-978b-4c93ef267db1" {
}
node sa_service_view_sa_confirm id "6f138672-149b-5f88-ab07-7fc413a42262" {
collapsed false;
diagram @sa_service_view;
element @sa_confirm;
position {
@@ -47,7 +40,6 @@ node sa_service_view_sa_confirm id "6f138672-149b-5f88-ab07-7fc413a42262" {
}
node sa_service_view_sa_control id "f53d4352-b042-56a6-8b3e-c51cd27254f9" {
collapsed false;
diagram @sa_service_view;
element @sa_control;
position {
@@ -61,7 +53,6 @@ node sa_service_view_sa_control id "f53d4352-b042-56a6-8b3e-c51cd27254f9" {
}
node sa_service_view_sa_repair id "ae36489d-6ec8-5f1c-b603-8b497227efba" {
collapsed false;
diagram @sa_service_view;
element @sa_repair;
position {
@@ -75,7 +66,6 @@ node sa_service_view_sa_repair id "ae36489d-6ec8-5f1c-b603-8b497227efba" {
}
node sa_service_view_sa_reset id "d52da4e6-40b7-5dbc-84fb-80c443711c69" {
collapsed false;
diagram @sa_service_view;
element @sa_reset;
position {
@@ -89,7 +79,6 @@ node sa_service_view_sa_reset id "d52da4e6-40b7-5dbc-84fb-80c443711c69" {
}
node sa_service_view_sa_technician id "40210589-69f7-5209-b329-10bb83cf615c" {
collapsed false;
diagram @sa_service_view;
element @sa_technician;
position {
@@ -6,20 +6,9 @@ diagram sa_state_view "5 · SA pressure supervision" id "30d5baf9-6581-5cb6-846a
machine @sa_monitor;
};
};
routes [];
view_options {
coverage "All";
exchange_labels "Name";
hidden_edges [];
participant_categories "All";
relation_kinds "All";
work_categories "All";
work_contexts "All";
};
}
node sa_state_view_sa_monitor id "a676fbb9-0833-59a8-98cf-a10d8f4b7434" {
collapsed false;
diagram @sa_state_view;
element @sa_monitor;
position {
@@ -33,7 +22,6 @@ node sa_state_view_sa_monitor id "a676fbb9-0833-59a8-98cf-a10d8f4b7434" {
}
node sa_state_view_sa_monitor_initial id "5e2326a0-0f6c-5f8d-8a21-fdded18a9281" {
collapsed false;
diagram @sa_state_view;
element @sa_monitor_initial;
position {
@@ -47,7 +35,6 @@ node sa_state_view_sa_monitor_initial id "5e2326a0-0f6c-5f8d-8a21-fdded18a9281"
}
node sa_state_view_sa_monitor_region id "55a74f91-2025-5001-8db8-9566b1dbe783" {
collapsed false;
diagram @sa_state_view;
element @sa_monitor_region;
position {
@@ -61,7 +48,6 @@ node sa_state_view_sa_monitor_region id "55a74f91-2025-5001-8db8-9566b1dbe783" {
}
node sa_state_view_sa_monitoring id "b3af7b04-5a45-5b82-ab69-7eada4b876f0" {
collapsed false;
diagram @sa_state_view;
element @sa_monitoring;
position {
@@ -75,7 +61,6 @@ node sa_state_view_sa_monitoring id "b3af7b04-5a45-5b82-ab69-7eada4b876f0" {
}
node sa_state_view_sa_waiting id "57acbb71-ca86-57fc-ae90-42dad195f0e0" {
collapsed false;
diagram @sa_state_view;
element @sa_waiting;
position {
@@ -0,0 +1,16 @@
# Incomplete production verification checkpoint
Read [the report](../../text-default-omission-production-20260913.md) before using
this contribution. `evidence.zip` retains two source ZIPs and manifests, the
focused success, the disk-limited broad build, independent execution of completed
test binaries, teaching regeneration/rerun, reviews and strict documentation
receipts. It does not establish complete core, desktop, service or release
acceptance. Original failures are retained.
Run `python -B verify.py` here for read-only integrity/outcome checks. The outer
`files.json` pins this archive; `capture-files.json` inside the ZIP pins its
members. `candidate/inputs.json` identifies the final checkpoint's Git-visible
source inputs; that candidate differs from executed captures as the report
explains. Obtain those candidate files from the same Git commit as this archive.
Producer scripts retain machine-local paths and need adaptation for new runs.
No binaries are distributed; their hashes are execution receipts.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
{
"candidate/inputs.json": {
"bytes": 102567,
"sha256": "8e5019d9e207735211518c7bc54f93483266cb2d3837aa7e22f2bf551972118d"
},
"evidence.zip": {
"bytes": 4964802,
"sha256": "cec613e8669797c74ce1e2b37526cb0050590e1fe10eb4ef1dbef94b0a958d90"
},
"README.md": {
"bytes": 1025,
"sha256": "612c6d37ce1c7ba871e1a0a13c459c28abba180e237415d3ceb4e89e0424ca44"
},
"verify.py": {
"bytes": 4295,
"sha256": "66c227c2e6f9d16364f30597aa2ca7496bed7e5fe68c9d914d30d9a5e651f718"
}
}
@@ -0,0 +1,57 @@
from pathlib import Path
import hashlib
import io
import json
import re
import zipfile
root = Path(__file__).resolve().parent
def digest(data): return {'bytes':len(data), 'sha256':hashlib.sha256(data).hexdigest()}
def require(ok, message):
if not ok: raise SystemExit(message)
manifest = json.loads((root / 'files.json').read_text())
actual = {p.relative_to(root).as_posix() for p in root.rglob('*') if p.is_file() and p.name != 'files.json' and '__pycache__' not in p.parts}
require(actual == set(manifest), 'Outer membership mismatch')
for name, expected in manifest.items(): require(digest((root/name).read_bytes()) == expected, f'Outer hash: {name}')
with zipfile.ZipFile(root / 'evidence.zip') as evidence:
def read(name): return json.loads(evidence.read(name))
payloads = read('capture-files.json')
require(set(evidence.namelist()) == set(payloads) | {'capture-files.json'}, 'Capture membership mismatch')
for name, expected in payloads.items(): require(digest(evidence.read(name)) == expected, f'Capture hash: {name}')
for gate, status in [('production-focused-1',0),('production-core-1',101)]:
result = read(gate+'/result.json')
require(result['exitCode'] == status and result['inputsUnchanged'], f'{gate}: outcome')
require(all(result[k] == v for k,v in read(gate+'/started.json').items()), f'{gate}: start')
for key, file in [('inputs','inputs.json'),('archive','inputs.zip'),('stdout','stdout.log'),('stderr','stderr.log')]:
require(digest(evidence.read(gate+'/'+file)) == result[key], f'{gate}: {key}')
require(digest(evidence.read('gate-policy.py')) == result['helper'], f'{gate}: runner')
inputs = read(gate+'/inputs.json')
require(len(inputs) == result['inputCount'], f'{gate}: input count')
with zipfile.ZipFile(io.BytesIO(evidence.read(gate+'/inputs.zip'))) as sources:
require(set(sources.namelist()) == set(inputs), f'{gate}: source membership')
for path, expected in inputs.items(): require(digest(sources.read(path)) == expected, f'{gate}: source {path}')
def totals(prefix, filenames):
rows = [tuple(map(int,m)) for f in filenames for m in re.findall(r'test result: (?:ok|FAILED)\. (\d+) passed; (\d+) failed; (\d+) ignored;', evidence.read(prefix+f).decode())]
return tuple(sum(row[i] for row in rows) for i in range(3))
require(totals('production-focused-1/', ['stdout.log']) == (75,0,0), 'Focused totals')
require(b'No space left on device' in evidence.read('production-core-1/stdout.log'), 'Missing disk failure evidence')
built = read('production-built-tests-1/results.json')
compiler = {r['cargo']['target']['name']:r for r in read('production-core-1/result.json')['artifacts']}
require(len(built) == 36, 'Built test inventory')
for run in built:
name = run['name']
require(run['binary'] == compiler[name]['readback'], f'{name}: binary receipt')
for stream in ['stdout','stderr']:
require(digest(evidence.read(f'production-built-tests-1/{name}.{stream}.log')) == run[stream], f'{name}: stream')
require(totals('production-built-tests-1/', [r['name']+'.stdout.log' for r in built]) == (354,2,1), 'Built test totals')
receipt = read('production-built-tests-1/receipt.json')
require(receipt['inputsUnchanged'] and receipt['testsRun'] == 36, 'Execution receipt')
teaching = read('production-teaching-1/result.json')
require(teaching['nonSourceFilesUnchanged'] and len(teaching['changed']) == 29, 'Teaching delta')
require(all(p.endswith('.arcadia') for p in teaching['changed']), 'Non-source teaching change')
for run in teaching['runs']:
require(run['exitCode'] == 0 and run['binary'] == compiler[run['name']]['readback'], 'Teaching runner receipt')
require(totals('production-teaching-1/', ['release1_teaching.stdout.log']) == (4,0,0), 'Teaching rerun')
require(read('language-doc-3/result.json')['warnings']['bytes'] == 0, 'Documentation warnings')
print('Verified captures: focused 75 pass; disk-limited build; separately executed 354 pass/2 fail/1 ignored; teaching rerun 4 pass.')
print('Final candidate still needs corrected anchor test, full core, strict Clippy, native/public-consumer and release verification.')
@@ -0,0 +1,52 @@
# Ordinary compact source: production checkpoint, verification incomplete
Branch: `codex/text-defaults-9e8r6t`, based on `2a64344b929077c873cb81c4f0b36c6bb3ae1ffe`. This checkpoint implements ordinary source defaults and lossless editing, but is **not ready for integration or release acceptance**. The focused production run passed 75 tests. The full core build exhausted disk space before all targets linked. Subsequent execution of 36 completed integration binaries yielded 354 passed, 2 failed, 1 ignored; one failure was subsequently resolved and rerun, while the other has a reviewed test correction awaiting compilation.
## Behavior and architecture
Current ordinary parsing and canonical flat/modular output share the explicit source-default inventory in `crates/oa-core/src/source_defaults.rs`. Required top-level fields retain explicit admission checks; selected constructors are not deep-merged with another constructor. Historical source2–6 parser paths remain separate. Source-specific Scenario defaults remain MixedLegacy, independent of GUI creation defaults. Signed zero, present-empty optional values, required empty payloads, and historical semantic records retain their distinctions.
`language::format_declarations(project, DeclarationFormat)` makes the editing/output seam explicit. `Compact` omits known defaults; `CompleteForEdit` restores serializer-skipped fields and does not prune; internal `CompactValues` retains declaration fields while compacting optional nested payloads for insertion. The public flat and modular writers use Compact. The updater compares complete old/new fields, uses compact templates only for inserted syntax, and emits compact new declarations through the existing structural updater. Migration makes the mechanically required explicit CompleteForEdit call with no insertion policy.
Unchanged authored defaults now survive payload edits, including Exchange.operation none, Node.port_anchors [], and Trace.baseline.semantic none. Their three previously failing witnesses passed and are enabled as normal regressions. An independent critic found that changing a relation kind can change an omitted orientation's meaning even when its typed value remains true. The correction reconsiders only implicit fields on a declaration-kind change; a valid BoundaryExchange-port to CommunicationMean-entity test confirms the required explicit true value appears while other source remains unchanged.
The prototype's 18 independent policy-map tests were moved into the private production module, with four additional controls. The public fidelity suite now exercises actual flat and generated modular exports rather than the experimental span-deletion bridge. Six output tests check typed field locations and required/default distinctions. Six update witnesses cover constructor insertion, required Signal/String payloads, optional route fields, signed-zero insertion and changed default context. The original prototype evidence remains available at its published commits and archives.
## Executed evidence
Environment: Windows, Rust/Cargo 1.98.1, `stable-x86_64-pc-windows-gnullvm`, configured LLVM-MinGW, one Cargo build job, incremental compilation disabled, locked offline dependencies. All process handles are closed.
| Capture | Outcome | Scope |
| --- | --- | --- |
| production-focused-1 | 75 passed, 0 failed, 0 ignored; exit 0 | Six integration targets, including all formerly ignored preservation witnesses; 614 unchanged inputs; 10:44:08–10:46:40 UTC |
| production-core-1 | Build failed, exit 101 | LLVM reported no space left on device while linking review_regressions; 613 unchanged inputs, 39 executable receipts; 10:49:54–11:00:46 UTC; no complete Cargo suite execution |
| production-built-tests-1 | 354 passed, 2 failed, 1 ignored | 36 integration binaries independently executed after exact hash checks against the closed compiler receipts; inputs unchanged; completed 11:03:09 UTC |
| production-teaching-1 | Generator exit 0; 4 teaching tests passed | Already-built and hash-verified generator/test executables, after updating current teaching source; 29 .arcadia files changed, every non-source teaching file unchanged |
| language-doc-3 | Strict Docutils 0.23, zero warnings | Final normative source and generated HTML, including correction of two stale explicit-default statements |
| production-format.log | cargo fmt --all -- --check: exit 0 | Final checkpoint Rust formatting |
The focused command was:
```text
cargo test --locked --offline -p oa-core --test default_omission_fidelity --test default_omission_output --test default_omission_updates --test default_omission_preservation --test default_omission_admission --test source_project --message-format=json -- --include-ignored --nocapture
```
The interrupted broad command was:
```text
cargo test --locked --offline -p oa-core --no-fail-fast --message-format=json -- --nocapture
```
The [portable evidence archive](experiments/default-production-20260913/README.md) preserves the two original source captures, raw Cargo streams and receipts, independently executed binary logs/receipts, generator results and before/after teaching hashes, review, documentation receipts, and producer scripts. Binary hashes are historical execution receipts; this checkpoint does not distribute those binaries. The archive verifier validates captured membership, source and stream hashes, and explicitly distinguishes build failure from separately executed tests.
## Findings and remaining validation
The teaching failure compared old verbose checked-in current-output examples with the new compact writer. The identified generator rebuilt the same model (94 elements, 62 relations, five views, 68 traces) and wrote 30 source files; 29 changed. Model JSON, transition requests/baselines, diagnostics, and other teaching files were byte-identical. Rerunning its unchanged identified test binary passed all four tests. Frozen historical compatibility fixtures were not regenerated.
The port-anchor test required removal of the enclosing authored field when its last manual entry was removed. Independent review found that this overconstrained lossless editing: retaining empty brackets and annotations is valid, while canonical export must omit the default field. The corrected test checks canonical omission, preserves complete-model/comment/re-add checks, and adds cleared no-op stability. **That correction and its previously unreached re-add half have not yet been compiled/run.**
The private 22-test policy/parser module, remaining full-suite targets, strict production Clippy, final native workflows, public consumer checks, and release packaging still require verification. Existing language negative tests that replace a now-omitted default (notably transition_kind External) must be reconciled with explicit invalid fixtures when those tests run. No production source8 acceptance is implied; that wiring remains the independent issue 7 contribution under the confirmed ownership partition (comments 949/951).
The new insertion metadata currently stores copied compact substrings. A read-only lifetime review confirmed a bounded improvement: retain the existing compact source string and map complete offsets to its Span values, avoiding repeated copies of enclosing records. **That improvement is planned but unapplied**, pending space to rebuild and verify it. Do not attribute this optimization to this checkpoint.
Disk recovery is externally constrained: automatic approval review rejected deletion of already-published duplicate local input ZIPs with “blocked by policy,” despite user authorization. No deletion occurred. A separate manually runnable cleanup script identifies 1,260,129,772 bytes of retired build artifacts and guards against active builds/newer files; it is local-only, not executed by the agent. User-facing cleanup is distinct from this reproducible code/evidence handoff. Linux checks remain explicitly user-paused. Issue 11 and the overall release objective remain open.