Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
684061ec0f |
@@ -12,6 +12,7 @@
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.zip binary
|
||||
harness/prerelease37-independent-9e8r6t-20260924/** -text whitespace=cr-at-eol
|
||||
|
||||
# Exact source/recovery oracles and archived ledgers retain their measured bytes.
|
||||
# Keep ordinary text diffs; only disable checkout/check-in newline conversion.
|
||||
|
||||
@@ -340,18 +340,6 @@ pub(crate) fn diagnostic(source: &str, error: Error) -> Diagnostic {
|
||||
},
|
||||
}
|
||||
}
|
||||
pub(crate) fn diagnostic_with_issue(
|
||||
source: &str,
|
||||
error: Error,
|
||||
issue: Option<validation::Issue>,
|
||||
) -> Diagnostic {
|
||||
let mut result = diagnostic(source, error);
|
||||
if let Some(issue) = issue {
|
||||
result.object_id = issue.element;
|
||||
result.field = issue.field;
|
||||
}
|
||||
result
|
||||
}
|
||||
fn reserved(word: &str) -> bool {
|
||||
crate::reserved_alias(word)
|
||||
|| declaration_kind(word).is_some()
|
||||
@@ -634,7 +622,6 @@ fn parse_document(source: &str, limits: ParseLimits) -> Result<ParsedDocument, V
|
||||
if tokens.get(1).is_some_and(|t| t.text(source) == "2") {
|
||||
return v2_source::parse(source, tokens, limits);
|
||||
}
|
||||
let mut migration_issue = None;
|
||||
Parser {
|
||||
source,
|
||||
tokens,
|
||||
@@ -642,8 +629,8 @@ fn parse_document(source: &str, limits: ParseLimits) -> Result<ParsedDocument, V
|
||||
value_depth: 0,
|
||||
limits,
|
||||
}
|
||||
.document(&mut migration_issue)
|
||||
.map_err(|e| vec![diagnostic_with_issue(source, e, migration_issue)])
|
||||
.document()
|
||||
.map_err(|e| vec![diagnostic(source, e)])
|
||||
}
|
||||
fn offset_for(source: &str, line: usize, column: usize) -> usize {
|
||||
let mut offset = 0;
|
||||
@@ -1462,10 +1449,7 @@ impl Parser<'_> {
|
||||
},
|
||||
}
|
||||
}
|
||||
fn document(
|
||||
&mut self,
|
||||
migration_issue: &mut Option<validation::Issue>,
|
||||
) -> Result<ParsedDocument, Error> {
|
||||
fn document(&mut self) -> Result<ParsedDocument, Error> {
|
||||
let unit = self.unit(true, false)?;
|
||||
let header = unit.header.expect("entry header");
|
||||
let mut aliases = BTreeMap::from([(header.project.alias.clone(), header.project.id)]);
|
||||
@@ -1492,14 +1476,9 @@ impl Parser<'_> {
|
||||
.map(|(key, span)| ((d.id, key.clone()), span.start)),
|
||||
);
|
||||
}
|
||||
let decoded = resolve_declarations(
|
||||
header.project,
|
||||
&unit.declarations,
|
||||
&aliases,
|
||||
self.limits,
|
||||
migration_issue,
|
||||
)
|
||||
.map_err(|(_, error)| error)?;
|
||||
let decoded =
|
||||
resolve_declarations(header.project, &unit.declarations, &aliases, self.limits)
|
||||
.map_err(|(_, error)| error)?;
|
||||
Ok(ParsedDocument {
|
||||
project: decoded.project,
|
||||
migration: decoded.migration,
|
||||
@@ -1703,7 +1682,6 @@ pub(crate) fn resolve_declarations(
|
||||
declarations: &[Declaration],
|
||||
aliases: &BTreeMap<String, Id>,
|
||||
limits: ParseLimits,
|
||||
migration_issue: &mut Option<validation::Issue>,
|
||||
) -> Result<crate::persistence::DecodeResult, (usize, Error)> {
|
||||
let legacy = project.schema_version == 3;
|
||||
let mut legacy_elements = serde_json::Map::new();
|
||||
@@ -2011,38 +1989,6 @@ pub(crate) fn resolve_declarations(
|
||||
crate::persistence::migrate_v6(project, limits)
|
||||
})
|
||||
.map_err(|error| {
|
||||
// These frozen admission checks formerly ran in final validation.
|
||||
// Keep their diagnostic code and reference location after moving
|
||||
// them before schema normalization.
|
||||
if let crate::persistence::FileError::Model { issues, .. } = &error
|
||||
&& let Some(issue) = issues.first()
|
||||
&& let Some(code) = match issue.code.as_str() {
|
||||
"diagram_layer" => Some("diagram_layer"),
|
||||
"view_options" => Some("view_options"),
|
||||
_ => None,
|
||||
}
|
||||
{
|
||||
let index = issue
|
||||
.element
|
||||
.and_then(|id| declarations.iter().position(|d| d.id == id))
|
||||
.unwrap_or(usize::MAX);
|
||||
let offset = declarations.get(index).map_or(0, |d| {
|
||||
issue
|
||||
.field
|
||||
.as_ref()
|
||||
.and_then(|field| d.field_spans.get(field))
|
||||
.map_or(d.offset, |span| span.start)
|
||||
});
|
||||
*migration_issue = Some(issue.clone());
|
||||
return (
|
||||
index,
|
||||
Error {
|
||||
code,
|
||||
offset,
|
||||
message: issue.message.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
(
|
||||
usize::MAX,
|
||||
Error {
|
||||
|
||||
@@ -69,8 +69,8 @@ impl Project4 {
|
||||
}
|
||||
crate::persistence::check_pre6_relations(self.relations.values())?;
|
||||
crate::persistence::check_pre6_elements(self.elements.values())?;
|
||||
let mut project = Project {
|
||||
schema_version: self.schema_version,
|
||||
Ok(Project {
|
||||
schema_version: SCHEMA_VERSION,
|
||||
id: self.id,
|
||||
alias: self.alias,
|
||||
name: self.name,
|
||||
@@ -84,11 +84,7 @@ impl Project4 {
|
||||
.into_iter()
|
||||
.map(|(id, trace)| (id, trace.into()))
|
||||
.collect(),
|
||||
};
|
||||
// Schema3's adapter also enters here, without calling `finish`.
|
||||
crate::persistence::check_pre7_diagram_admission(&project)?;
|
||||
project.schema_version = SCHEMA_VERSION;
|
||||
Ok(project)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +104,6 @@ pub(crate) fn review_defaults(project: &Project, report: &mut MigrationReport) {
|
||||
}
|
||||
|
||||
pub(crate) fn finish(mut project: Project, limits: ParseLimits) -> Result<DecodeResult, FileError> {
|
||||
// Source4 enters here directly rather than through `Project4`.
|
||||
crate::persistence::check_pre7_diagram_admission(&project)?;
|
||||
crate::persistence::check_pre6_relations(project.relations.values())?;
|
||||
crate::persistence::check_pre6_elements(project.elements.values())?;
|
||||
project.schema_version = SCHEMA_VERSION;
|
||||
|
||||
@@ -341,50 +341,6 @@ pub(crate) fn check_pre6_baseline(baseline: &SemanticBaseline) -> Result<(), Fil
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
/// Frozen sources 3–6 admit opposite-layer placements and visibility references
|
||||
/// only in Traceability. Check before migration discards the advertised version;
|
||||
/// current ordinary projections may retain these records as latent presentation.
|
||||
pub(crate) fn check_pre7_diagram_admission(project: &Project) -> Result<(), FileError> {
|
||||
let mut issues = Vec::new();
|
||||
for diagram in project.diagrams.values() {
|
||||
let option_issues = if diagram.projection == ProjectionKind::Traceability {
|
||||
crate::view_options::validate_options_cross_layer(project, &diagram.view_options)
|
||||
} else {
|
||||
crate::view_options::validate_options(project, diagram.layer, &diagram.view_options)
|
||||
};
|
||||
for issue in option_issues {
|
||||
validation::error(
|
||||
&mut issues,
|
||||
diagram.id,
|
||||
"view_options",
|
||||
issue.field,
|
||||
issue.message,
|
||||
);
|
||||
}
|
||||
for node in diagram.nodes.values() {
|
||||
if !project.elements.get(&node.element).is_some_and(|element| {
|
||||
diagram.projection == ProjectionKind::Traceability
|
||||
|| element.layer() == diagram.layer
|
||||
}) {
|
||||
validation::error(
|
||||
&mut issues,
|
||||
node.id,
|
||||
"diagram_layer",
|
||||
"element",
|
||||
"Diagram nodes reference existing elements in the selected layer.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(issue) = issues.into_iter().next() {
|
||||
return Err(FileError::Model {
|
||||
message: issue.message.clone(),
|
||||
issues: vec![issue],
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn migrate_v5(
|
||||
mut project: Project,
|
||||
limits: crate::language::ParseLimits,
|
||||
@@ -392,7 +348,6 @@ pub(crate) fn migrate_v5(
|
||||
if project.schema_version != 5 {
|
||||
return Err(FileError::Invalid("Expected schema_version 5.".into()));
|
||||
}
|
||||
check_pre7_diagram_admission(&project)?;
|
||||
check_pre6_relations(project.relations.values())?;
|
||||
check_pre6_elements(project.elements.values())?;
|
||||
for baseline in project
|
||||
@@ -465,7 +420,6 @@ pub(crate) fn migrate_v6(
|
||||
if project.schema_version != 6 {
|
||||
return Err(FileError::Invalid("Expected schema_version 6.".into()));
|
||||
}
|
||||
check_pre7_diagram_admission(&project)?;
|
||||
reject_source7_fields(&serde_json::to_value(&project)?).map_err(FileError::Invalid)?;
|
||||
limits.check_model(&project).map_err(FileError::Limit)?;
|
||||
project.schema_version = SCHEMA_VERSION;
|
||||
|
||||
@@ -85,7 +85,6 @@ pub(crate) fn parents(p: &Project, d: &Diagram) -> BTreeMap<Id, Option<Id>> {
|
||||
.filter(|n| {
|
||||
p.elements.get(&n.element).is_some_and(|e| {
|
||||
e.kind() != Kind::Port
|
||||
&& (d.projection == ProjectionKind::Traceability || e.layer() == d.layer)
|
||||
&& d.projection.includes(e.kind())
|
||||
&& d.projection
|
||||
.state_machine()
|
||||
|
||||
@@ -589,17 +589,6 @@ fn apply_command(
|
||||
.map(|d| d.id)
|
||||
.ok_or_else(missing)?;
|
||||
if source != *diagram {
|
||||
let destination = &p.diagrams[diagram];
|
||||
let element = p.diagrams[&source].nodes[node].element;
|
||||
if destination.projection != ProjectionKind::Traceability
|
||||
&& p.elements
|
||||
.get(&element)
|
||||
.is_none_or(|e| e.layer() != destination.layer)
|
||||
{
|
||||
return Err(ModelError(
|
||||
"Move the placement to a view in its layer or a traceability view.".into(),
|
||||
));
|
||||
}
|
||||
let placement = p
|
||||
.diagrams
|
||||
.get_mut(&source)
|
||||
|
||||
@@ -573,26 +573,16 @@ fn compile_once(
|
||||
span,
|
||||
}),
|
||||
};
|
||||
let mut migration_issue = None;
|
||||
let decoded = language::resolve_declarations(
|
||||
header.project,
|
||||
&declarations,
|
||||
&aliases,
|
||||
limits.model,
|
||||
&mut migration_issue,
|
||||
)
|
||||
.map_err(|(index, error)| {
|
||||
let path = declaration_paths.get(index).unwrap_or(&bundle.entry);
|
||||
vec![SourceDiagnostic {
|
||||
path: path.clone(),
|
||||
diagnostic: language::diagnostic_with_issue(
|
||||
&bundle.files[path],
|
||||
error,
|
||||
migration_issue,
|
||||
),
|
||||
related: Vec::new(),
|
||||
}]
|
||||
})?;
|
||||
let decoded =
|
||||
language::resolve_declarations(header.project, &declarations, &aliases, limits.model)
|
||||
.map_err(|(index, error)| {
|
||||
let path = declaration_paths.get(index).unwrap_or(&bundle.entry);
|
||||
vec![SourceDiagnostic {
|
||||
path: path.clone(),
|
||||
diagnostic: language::diagnostic(&bundle.files[path], error),
|
||||
related: Vec::new(),
|
||||
}]
|
||||
})?;
|
||||
let diagnostics = model_diagnostics(bundle, &index, &decoded.project);
|
||||
let errors: Vec<_> = diagnostics
|
||||
.iter()
|
||||
|
||||
@@ -620,14 +620,11 @@ pub fn validate(p: &Project) -> Vec<Issue> {
|
||||
text(&mut out, d.id, "name", &d.name);
|
||||
text(&mut out, d.id, "description", &d.description);
|
||||
crate::scenario_layout::validate_diagram(p, d, &mut out);
|
||||
// Ordinary projections are reversible filters over the same authored view.
|
||||
// Retain opposite-layer visibility choices when leaving Traceability.
|
||||
let option_issues =
|
||||
if d.projection.state_machine().is_none() && d.projection.scenario().is_none() {
|
||||
crate::view_options::validate_options_cross_layer(p, &d.view_options)
|
||||
} else {
|
||||
crate::view_options::validate_options(p, d.layer, &d.view_options)
|
||||
};
|
||||
let option_issues = if d.projection == ProjectionKind::Traceability {
|
||||
crate::view_options::validate_options_cross_layer(p, &d.view_options)
|
||||
} else {
|
||||
crate::view_options::validate_options(p, d.layer, &d.view_options)
|
||||
};
|
||||
for issue in option_issues {
|
||||
error(&mut out, d.id, "view_options", issue.field, issue.message);
|
||||
}
|
||||
@@ -801,10 +798,8 @@ pub fn validate(p: &Project) -> Vec<Issue> {
|
||||
);
|
||||
}
|
||||
match p.elements.get(&n.element) {
|
||||
Some(e)
|
||||
if (d.projection.state_machine().is_none()
|
||||
&& d.projection.scenario().is_none())
|
||||
|| e.layer() == d.layer => {}
|
||||
Some(e) if d.projection == ProjectionKind::Traceability || e.layer() == d.layer => {
|
||||
}
|
||||
_ => error(
|
||||
&mut out,
|
||||
n.id,
|
||||
|
||||
@@ -218,9 +218,7 @@ pub fn validate_options(p: &Project, layer: Layer, options: &ViewOptions) -> Vec
|
||||
validate_options_in(p, Some(layer), options)
|
||||
}
|
||||
|
||||
/// Ordinary saved views retain references from either architecture layer so
|
||||
/// projection changes preserve authored visibility choices. Projection decides
|
||||
/// which layer is visible; typed State/Scenario views use layer-scoped validation.
|
||||
/// Traceability views admit references from either architecture layer.
|
||||
pub fn validate_options_cross_layer(p: &Project, options: &ViewOptions) -> Vec<OptionError> {
|
||||
validate_options_in(p, None, options)
|
||||
}
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
//! Current latent presentation must not broaden frozen source/JSON admission.
|
||||
use oa_core::{view_options::*, *};
|
||||
|
||||
fn legacy_json(project: &Project, version: u32) -> Vec<u8> {
|
||||
let mut value = serde_json::to_value(project).unwrap();
|
||||
value["schema_version"] = version.into();
|
||||
for diagram in value["diagrams"].as_object_mut().unwrap().values_mut() {
|
||||
let fields = diagram.as_object_mut().unwrap();
|
||||
fields.remove("layout");
|
||||
if version < 6 {
|
||||
fields.remove("view_options");
|
||||
}
|
||||
}
|
||||
serde_json::to_vec(&value).unwrap()
|
||||
}
|
||||
|
||||
fn model_issue(error: persistence::FileError) -> validation::Issue {
|
||||
match error {
|
||||
persistence::FileError::Model { mut issues, .. } => issues.remove(0),
|
||||
other => panic!("Expected a located model issue, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frozen_3_through_6_reject_cross_layer_nodes_but_keep_mixed_traceability() {
|
||||
let node_id = Id::parse_str("00000000-0000-4000-8000-000000000004").unwrap();
|
||||
for version in 3..=6 {
|
||||
let source = format!(
|
||||
"arcadia {version}; project compat \"Compatibility\" id \"00000000-0000-4000-8000-000000000001\" {{\n\
|
||||
sa_function work \"Counterpart\" id \"00000000-0000-4000-8000-000000000002\" {{}}\n\
|
||||
diagram view \"OA concern\" id \"00000000-0000-4000-8000-000000000003\" {{ layer Oa; projection Traceability; }}\n\
|
||||
node occurrence id \"00000000-0000-4000-8000-000000000004\" {{ diagram @view; element @work; }}\n}}"
|
||||
);
|
||||
let mixed = language::parse(&source).unwrap();
|
||||
assert_eq!(
|
||||
persistence::decode(&legacy_json(&mixed, version)).unwrap(),
|
||||
mixed
|
||||
);
|
||||
|
||||
let ordinary = source.replace("projection Traceability", "projection Overview");
|
||||
let same_layer = ordinary.replace("layer Oa", "layer Sa");
|
||||
let ordinary_project = language::parse(&same_layer).unwrap();
|
||||
assert_eq!(
|
||||
persistence::decode(&legacy_json(&ordinary_project, version)).unwrap(),
|
||||
ordinary_project
|
||||
);
|
||||
let errors = language::parse(&ordinary).unwrap_err();
|
||||
// Source3 already reported migration errors with the offending location.
|
||||
let code = if version == 3 {
|
||||
"migration_error"
|
||||
} else {
|
||||
"diagram_layer"
|
||||
};
|
||||
assert!(
|
||||
errors.iter().any(|error| error.code == code
|
||||
&& error.line == 4
|
||||
&& error.message.contains("selected layer")),
|
||||
"{version}: {errors:?}"
|
||||
);
|
||||
|
||||
let mut latent = mixed.clone();
|
||||
let diagram = latent.diagrams.values_mut().next().unwrap();
|
||||
diagram.projection = ProjectionKind::Overview;
|
||||
let node = diagram.nodes.values().next().unwrap().id;
|
||||
assert_eq!(node, node_id);
|
||||
if version >= 4 {
|
||||
let diagnostic = errors
|
||||
.iter()
|
||||
.find(|error| error.code == "diagram_layer")
|
||||
.unwrap();
|
||||
assert_eq!(diagnostic.object_id, Some(node_id));
|
||||
assert_eq!(diagnostic.field.as_deref(), Some("element"));
|
||||
let bundle = source_project::SourceBundle {
|
||||
entry: "model.arcadia".into(),
|
||||
files: std::collections::BTreeMap::from([("model.arcadia".into(), ordinary)]),
|
||||
};
|
||||
let errors =
|
||||
source_project::compile_bundle(&bundle, source_project::ProjectLimits::default())
|
||||
.unwrap_err();
|
||||
let diagnostic = &errors
|
||||
.iter()
|
||||
.find(|error| error.diagnostic.code == "diagram_layer")
|
||||
.unwrap()
|
||||
.diagnostic;
|
||||
assert_eq!(diagnostic.object_id, Some(node_id));
|
||||
assert_eq!(diagnostic.field.as_deref(), Some("element"));
|
||||
}
|
||||
let issue = model_issue(persistence::decode(&legacy_json(&latent, version)).unwrap_err());
|
||||
assert_eq!(issue.code, "diagram_layer");
|
||||
assert_eq!(issue.element, Some(node));
|
||||
assert_eq!(issue.field.as_deref(), Some("element"));
|
||||
// The exact same record is valid under the current presentation contract.
|
||||
assert_eq!(language::parse(&language::format(&latent)).unwrap(), latent);
|
||||
assert_eq!(
|
||||
persistence::decode(&serde_json::to_vec(&latent).unwrap()).unwrap(),
|
||||
latent
|
||||
);
|
||||
let mut dangling = mixed;
|
||||
dangling
|
||||
.diagrams
|
||||
.values_mut()
|
||||
.next()
|
||||
.unwrap()
|
||||
.nodes
|
||||
.values_mut()
|
||||
.next()
|
||||
.unwrap()
|
||||
.element = Id::parse_str("00000000-0000-4000-8000-000000000005").unwrap();
|
||||
let issue = model_issue(persistence::decode(&legacy_json(&dangling, version)).unwrap_err());
|
||||
assert_eq!(issue.code, "diagram_layer");
|
||||
assert_eq!(issue.element, Some(node));
|
||||
assert_eq!(issue.field.as_deref(), Some("element"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frozen_6_checks_every_layer_sensitive_saved_selector_before_upgrade() {
|
||||
let mut project = Project::empty();
|
||||
let a = project
|
||||
.add_element(Kind::Activity, Layer::Oa, "A", None)
|
||||
.unwrap();
|
||||
let b = project
|
||||
.add_element(Kind::Activity, Layer::Oa, "B", None)
|
||||
.unwrap();
|
||||
let edge = project.relate(RelationKind::Interaction, "Interaction", a, b);
|
||||
let category = project
|
||||
.add_element(Kind::ExchangeCategory, Layer::Oa, "Work category", None)
|
||||
.unwrap();
|
||||
let participant_category = project
|
||||
.add_element(
|
||||
Kind::ExchangeCategory,
|
||||
Layer::Oa,
|
||||
"Participant category",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
*project
|
||||
.elements
|
||||
.get_mut(&participant_category)
|
||||
.unwrap()
|
||||
.shared_mut()
|
||||
.unwrap() = SharedBody::ExchangeCategory {
|
||||
family: ExchangeFamily::Participant,
|
||||
exchanges: vec![],
|
||||
};
|
||||
let context = project
|
||||
.add_element(Kind::Process, Layer::Oa, "Process", None)
|
||||
.unwrap();
|
||||
let diagram = project.ensure_diagram(Layer::Sa);
|
||||
for (field, options) in [
|
||||
(
|
||||
"hidden_edges",
|
||||
ViewOptions {
|
||||
hidden_edges: vec![edge],
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"work_categories",
|
||||
ViewOptions {
|
||||
work_categories: CategorySelection::Any(vec![category]),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"participant_categories",
|
||||
ViewOptions {
|
||||
participant_categories: CategorySelection::Any(vec![participant_category]),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"work_contexts",
|
||||
ViewOptions {
|
||||
work_contexts: ContextSelection::Any(vec![context]),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
] {
|
||||
project.diagrams.get_mut(&diagram).unwrap().view_options = options;
|
||||
let issue = model_issue(persistence::decode(&legacy_json(&project, 6)).unwrap_err());
|
||||
assert_eq!(issue.code, "view_options");
|
||||
assert_eq!(issue.element, Some(diagram));
|
||||
assert_eq!(issue.field.as_deref(), Some(field));
|
||||
let current_source = language::format(&project);
|
||||
assert_eq!(language::parse(¤t_source).unwrap(), project);
|
||||
let source6 = current_source.replacen("arcadia 7;", "arcadia 6;", 1);
|
||||
let errors = language::parse(&source6).unwrap_err();
|
||||
let diagnostic = errors
|
||||
.iter()
|
||||
.find(|error| error.code == "view_options")
|
||||
.unwrap();
|
||||
assert_eq!(diagnostic.object_id, Some(diagram));
|
||||
assert_eq!(diagnostic.field.as_deref(), Some(field));
|
||||
|
||||
project.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Traceability;
|
||||
assert_eq!(
|
||||
persistence::decode(&legacy_json(&project, 6)).unwrap(),
|
||||
project
|
||||
);
|
||||
let source6 = language::format(&project).replacen("arcadia 7;", "arcadia 6;", 1);
|
||||
assert_eq!(language::parse(&source6).unwrap(), project);
|
||||
project.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Overview;
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
use oa_core::{source_project::*, view_options::*, *};
|
||||
|
||||
fn fixture(layer: Layer) -> (Project, Id, Id, Id, Id) {
|
||||
let mut p = Project::empty();
|
||||
let oa = p
|
||||
.add_element(Kind::Activity, Layer::Oa, "Assess alarm", None)
|
||||
.unwrap();
|
||||
let oa2 = p
|
||||
.add_element(Kind::Activity, Layer::Oa, "Respond", None)
|
||||
.unwrap();
|
||||
let sa = p
|
||||
.add_element(Kind::Function, Layer::Sa, "Assess alarm", None)
|
||||
.unwrap();
|
||||
let edge = p.relate(RelationKind::Interaction, "Alarm", oa, oa2);
|
||||
let category = p
|
||||
.add_element(Kind::ExchangeCategory, Layer::Oa, "Alarm data", None)
|
||||
.unwrap();
|
||||
*p.elements.get_mut(&category).unwrap().shared_mut().unwrap() = SharedBody::ExchangeCategory {
|
||||
family: ExchangeFamily::Work,
|
||||
exchanges: vec![edge],
|
||||
};
|
||||
let context = p
|
||||
.add_element(Kind::Process, Layer::Oa, "Alarm handling", None)
|
||||
.unwrap();
|
||||
let d = p.ensure_diagram(layer);
|
||||
p.diagrams.get_mut(&d).unwrap().projection = ProjectionKind::Traceability;
|
||||
for (i, element) in [oa, oa2, sa].into_iter().enumerate() {
|
||||
p.place(
|
||||
d,
|
||||
element,
|
||||
Position {
|
||||
x: 300. * i as f32,
|
||||
y: 40.,
|
||||
},
|
||||
);
|
||||
}
|
||||
p.diagrams.get_mut(&d).unwrap().view_options = ViewOptions {
|
||||
hidden_edges: vec![edge],
|
||||
work_categories: CategorySelection::Any(vec![category]),
|
||||
work_contexts: ContextSelection::Any(vec![context]),
|
||||
..Default::default()
|
||||
};
|
||||
validate_candidate(&p).unwrap();
|
||||
(p, d, oa, sa, category)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn projection_switching_preserves_complete_model_history_and_all_serializations() {
|
||||
for layer in [Layer::Oa, Layer::Sa] {
|
||||
let (baseline, diagram, _, _, _) = fixture(layer);
|
||||
for projection in [
|
||||
ProjectionKind::Overview,
|
||||
ProjectionKind::Decomposition,
|
||||
ProjectionKind::Allocation,
|
||||
ProjectionKind::FunctionalDataflow,
|
||||
ProjectionKind::Capabilities,
|
||||
] {
|
||||
let mut expected = baseline.clone();
|
||||
expected.diagrams.get_mut(&diagram).unwrap().projection = projection;
|
||||
let mut session = Session::new(baseline.clone());
|
||||
session
|
||||
.execute(Command::EditDiagram(
|
||||
diagram,
|
||||
expected.diagrams[&diagram].clone(),
|
||||
))
|
||||
.unwrap();
|
||||
assert_eq!(session.project(), &expected);
|
||||
let view = projection::project_view(session.project(), diagram);
|
||||
assert!(
|
||||
view.nodes
|
||||
.iter()
|
||||
.all(|n| expected.elements[&n.element].layer() == layer)
|
||||
);
|
||||
assert_eq!(
|
||||
view,
|
||||
projection::project_view_with_index(&expected, diagram, &ViewIndex::new(&expected))
|
||||
);
|
||||
let source = language::format(&expected);
|
||||
assert_eq!(language::parse(&source).unwrap(), expected);
|
||||
assert_eq!(
|
||||
persistence::decode(&serde_json::to_vec(&expected).unwrap()).unwrap(),
|
||||
expected
|
||||
);
|
||||
let bundle = format_bundle(&expected);
|
||||
assert_eq!(
|
||||
compile_bundle(&bundle, ProjectLimits::default())
|
||||
.unwrap()
|
||||
.decoded
|
||||
.project,
|
||||
expected
|
||||
);
|
||||
session.undo();
|
||||
assert_eq!(session.project(), &baseline);
|
||||
assert!(!session.can_undo());
|
||||
session.redo();
|
||||
assert_eq!(session.project(), &expected);
|
||||
session
|
||||
.execute(Command::EditDiagram(
|
||||
diagram,
|
||||
baseline.diagrams[&diagram].clone(),
|
||||
))
|
||||
.unwrap();
|
||||
assert_eq!(session.project(), &baseline);
|
||||
assert_eq!(
|
||||
projection::project_view(session.project(), diagram),
|
||||
projection::project_view(&baseline, diagram)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn projection_switching_does_not_admit_missing_elements_or_invalid_filter_kinds() {
|
||||
let (mut p, d, oa, _, category) = fixture(Layer::Sa);
|
||||
p.diagrams.get_mut(&d).unwrap().projection = ProjectionKind::Allocation;
|
||||
validate_candidate(&p).unwrap();
|
||||
let valid = p.clone();
|
||||
let node = p.diagrams[&d]
|
||||
.nodes
|
||||
.values()
|
||||
.find(|n| n.element == oa)
|
||||
.unwrap()
|
||||
.id;
|
||||
p.diagrams
|
||||
.get_mut(&d)
|
||||
.unwrap()
|
||||
.nodes
|
||||
.get_mut(&node)
|
||||
.unwrap()
|
||||
.element = Id::new_v4();
|
||||
assert!(validate_candidate(&p).is_err());
|
||||
p = valid.clone();
|
||||
p.diagrams.get_mut(&d).unwrap().view_options.work_categories = CategorySelection::Any(vec![oa]);
|
||||
assert!(validate_candidate(&p).is_err());
|
||||
p = valid;
|
||||
p.elements.remove(&category);
|
||||
assert!(validate_candidate(&p).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn projection_switching_keeps_typed_state_membership_strict() {
|
||||
let (mut p, d, _, _, _) = fixture(Layer::Sa);
|
||||
let system = p
|
||||
.add_element(Kind::System, Layer::Sa, "System", None)
|
||||
.unwrap();
|
||||
let machine = p
|
||||
.add_element(Kind::StateMachine, Layer::Sa, "Modes", Some(system))
|
||||
.unwrap();
|
||||
p.diagrams.get_mut(&d).unwrap().projection = ProjectionKind::StateMachine { machine };
|
||||
assert!(
|
||||
validation::validate(&p)
|
||||
.iter()
|
||||
.any(|i| i.code == "state_diagram_member")
|
||||
);
|
||||
assert!(validate_candidate(&p).is_err());
|
||||
}
|
||||
@@ -67,18 +67,7 @@ fn source6_traceability_default_keeps_both_layers_and_indexed_projection_matches
|
||||
.any(|e| e.id == interaction)
|
||||
);
|
||||
p.diagrams.get_mut(&d).unwrap().projection = ProjectionKind::Overview;
|
||||
validate_candidate(&p).unwrap();
|
||||
let ordinary = projection::project_view_with_index(&p, d, &index);
|
||||
assert!(
|
||||
ordinary
|
||||
.nodes
|
||||
.iter()
|
||||
.all(|n| p.elements[&n.element].layer() == Layer::Oa)
|
||||
);
|
||||
assert!(ordinary.edges.iter().any(|e| e.id == interaction));
|
||||
assert!(!ordinary.edges.iter().any(|e| e.id == fe));
|
||||
assert_eq!(p.diagrams[&d].nodes.len(), 4);
|
||||
assert_eq!(p.diagrams[&d].view_options.hidden_edges, vec![fe]);
|
||||
assert!(validate_candidate(&p).is_err());
|
||||
p.diagrams.get_mut(&d).unwrap().projection = ProjectionKind::Traceability;
|
||||
p.diagrams.get_mut(&d).unwrap().view_options.relation_kinds = RelationKinds::Only(vec![]);
|
||||
assert!(
|
||||
|
||||
@@ -4,8 +4,6 @@ pub(crate) mod architecture_authoring;
|
||||
mod architecture_authoring_tests;
|
||||
#[cfg(test)]
|
||||
mod behavior_removal_tests;
|
||||
#[cfg(test)]
|
||||
mod canvas_selection_tests;
|
||||
mod checkpoints;
|
||||
#[cfg(test)]
|
||||
mod diagram_focus_tests;
|
||||
@@ -383,7 +381,6 @@ pub enum Message {
|
||||
Behavior(crate::behavior::Event),
|
||||
Layer(Layer),
|
||||
Select(Id),
|
||||
CanvasSelect(crate::route_edit::Context, Id),
|
||||
EditProperties(Id),
|
||||
SelectNode(Id),
|
||||
DiagramSelection(multiselect::Event),
|
||||
@@ -1174,13 +1171,7 @@ impl App {
|
||||
if id != self.selected {
|
||||
self.selected_attachment = None;
|
||||
}
|
||||
if self.selected == id
|
||||
&& !process_preview
|
||||
&& self
|
||||
.project()
|
||||
.layer_of(id)
|
||||
.is_none_or(|layer| layer == self.layer)
|
||||
{
|
||||
if self.selected == id && !process_preview {
|
||||
return;
|
||||
}
|
||||
if !self.apply() {
|
||||
@@ -1226,24 +1217,6 @@ impl App {
|
||||
}
|
||||
self.refresh_draft();
|
||||
}
|
||||
/// Inspect an already validated canvas target without following its model layer.
|
||||
fn inspect_canvas_target(&mut self, id: Id) -> bool {
|
||||
if self.selected == id {
|
||||
return true;
|
||||
}
|
||||
if !self.project().contains(id) || !self.apply() {
|
||||
return false;
|
||||
}
|
||||
if !self.project().contains(id) {
|
||||
return false;
|
||||
}
|
||||
self.selected = id;
|
||||
self.selected_node = None;
|
||||
self.selected_attachment = None;
|
||||
self.selected_bend = None;
|
||||
self.refresh_draft();
|
||||
true
|
||||
}
|
||||
fn reveal_explorer_selection(&mut self) {
|
||||
if let Some(group) = self.explorer_index.group(self.selected) {
|
||||
self.explorer_groups_collapsed.remove(&group);
|
||||
@@ -1390,11 +1363,6 @@ impl App {
|
||||
{
|
||||
return Task::none();
|
||||
}
|
||||
if let Message::CanvasSelect(captured, _) = &message
|
||||
&& (self.page != Page::Diagram || self.route_context() != Some(*captured))
|
||||
{
|
||||
return Task::none();
|
||||
}
|
||||
// Selection can change without advancing the captured canvas context.
|
||||
if let Message::Route(
|
||||
crate::route_edit::Event::Commit(captured, changed, ..)
|
||||
@@ -2150,7 +2118,6 @@ impl App {
|
||||
Message::Command(Command::Layout(diagram)) => self.saved_scenario_diagram(*diagram),
|
||||
Message::Projection(projection) => active || projection.scenario().is_some(),
|
||||
Message::SelectNode(_)
|
||||
| Message::CanvasSelect(..)
|
||||
| Message::PlaceSelected
|
||||
| Message::HideSelected
|
||||
| Message::ToggleCollapse
|
||||
@@ -2751,10 +2718,10 @@ impl App {
|
||||
e.kind() != Kind::Port && d.projection.includes(e.kind())
|
||||
})
|
||||
});
|
||||
if show_in_mixed_view || menu.request.canvas.is_some() {
|
||||
if !self.inspect_canvas_target(menu.request.object) {
|
||||
return Task::none();
|
||||
}
|
||||
if show_in_mixed_view {
|
||||
self.selected = menu.request.object;
|
||||
self.selected_attachment = None;
|
||||
self.refresh_draft();
|
||||
} else {
|
||||
self.select(menu.request.object);
|
||||
}
|
||||
@@ -2766,15 +2733,7 @@ impl App {
|
||||
}
|
||||
self.reconcile_multiselect();
|
||||
self.selected_bend = menu.request.bend.map(|i| (menu.request.object, i));
|
||||
let action = match *action {
|
||||
Message::EditProperties(id)
|
||||
if menu.request.canvas.is_some() && id == menu.request.object =>
|
||||
{
|
||||
Message::InspectorTab(crate::navigation::InspectorTab::Properties)
|
||||
}
|
||||
action => action,
|
||||
};
|
||||
return self.update(action);
|
||||
return self.update(*action);
|
||||
}
|
||||
Message::CreateContextElement(kind, child) => {
|
||||
self.create_kind = kind;
|
||||
@@ -2930,14 +2889,6 @@ impl App {
|
||||
self.status = "Property draft reverted".into();
|
||||
}
|
||||
Message::Select(id) => self.select(id),
|
||||
Message::CanvasSelect(context, id) => {
|
||||
let view = project_view(self.project(), context.diagram);
|
||||
if view.ports.iter().any(|port| port.element == id)
|
||||
|| view.edges.iter().any(|edge| edge.id == id)
|
||||
{
|
||||
self.inspect_canvas_target(id);
|
||||
}
|
||||
}
|
||||
Message::DiagramSelection(event) => self.multiselect_event(event),
|
||||
Message::EditProperties(id) => {
|
||||
self.select(id);
|
||||
@@ -3385,7 +3336,7 @@ impl App {
|
||||
self.workflow = Some(crate::workflow::Workflow::relationship_kind(
|
||||
self.project(),
|
||||
self.selected,
|
||||
self.project().layer_of(self.selected).unwrap_or(self.layer),
|
||||
self.layer,
|
||||
kind,
|
||||
incoming,
|
||||
));
|
||||
@@ -4411,18 +4362,13 @@ impl App {
|
||||
self.status = "Select an SA function or participant, then choose a typed port action in Properties.".into();
|
||||
return;
|
||||
}
|
||||
let creation_layer = if child {
|
||||
self.project().layer_of(self.selected).unwrap_or(self.layer)
|
||||
} else {
|
||||
self.layer
|
||||
};
|
||||
let allocated = child
|
||||
&& self.create_kind.is_work()
|
||||
&& self
|
||||
.project()
|
||||
.elements
|
||||
.get(&self.selected)
|
||||
.is_some_and(|e| e.layer() == creation_layer && e.kind().is_performer());
|
||||
.is_some_and(|e| e.layer() == self.layer && e.kind().is_performer());
|
||||
let parent = if child && !allocated {
|
||||
Some(self.selected)
|
||||
} else {
|
||||
@@ -4430,7 +4376,7 @@ impl App {
|
||||
};
|
||||
let Some(e) = self.project().make_element(
|
||||
self.create_kind,
|
||||
creation_layer,
|
||||
self.layer,
|
||||
&format!("New {}", self.create_kind.label().to_lowercase()),
|
||||
parent,
|
||||
) else {
|
||||
@@ -4459,16 +4405,16 @@ impl App {
|
||||
.project()
|
||||
.diagrams
|
||||
.values()
|
||||
.find(|d| d.layer == creation_layer && d.projection == preferred)
|
||||
.find(|d| d.layer == self.layer && d.projection == preferred)
|
||||
.map(|d| d.id);
|
||||
if diagram_target.is_none() {
|
||||
let view = Id::new_v4();
|
||||
commands.push(Command::CreateDiagram(Diagram {
|
||||
id: view,
|
||||
alias: self.project().fresh_alias("engineering_view", "view", view),
|
||||
name: format!("{} {preferred}", creation_layer),
|
||||
name: format!("{} {preferred}", self.layer),
|
||||
description: String::new(),
|
||||
layer: creation_layer,
|
||||
layer: self.layer,
|
||||
projection: preferred,
|
||||
layout: None,
|
||||
routes: Vec::new(),
|
||||
@@ -4601,13 +4547,7 @@ impl App {
|
||||
));
|
||||
}
|
||||
if self.execute(Command::Batch(commands)) {
|
||||
let changed_view = self.active_diagram != diagram_target;
|
||||
self.active_diagram = diagram_target;
|
||||
if changed_view
|
||||
&& let Some(view) = diagram_target.and_then(|id| self.project().diagrams.get(&id))
|
||||
{
|
||||
self.layer = view.layer;
|
||||
}
|
||||
self.selected = id;
|
||||
self.inspector_tab = crate::navigation::InspectorTab::Properties;
|
||||
self.refresh_draft();
|
||||
@@ -4621,7 +4561,7 @@ impl App {
|
||||
self.workflow = Some(crate::workflow::Workflow::relationship(
|
||||
self.project(),
|
||||
self.selected,
|
||||
self.project().layer_of(self.selected).unwrap_or(self.layer),
|
||||
self.layer,
|
||||
));
|
||||
}
|
||||
fn add_trace(&mut self) {
|
||||
@@ -5146,9 +5086,17 @@ impl App {
|
||||
&project_view(self.project(), context.diagram),
|
||||
id,
|
||||
);
|
||||
if !self.inspect_canvas_target(id) {
|
||||
self.connection = Some(session);
|
||||
return;
|
||||
if session.tool == crate::connection_tool::Tool::Allocation
|
||||
&& self.project().diagrams[&context.diagram].projection
|
||||
== ProjectionKind::Traceability
|
||||
{
|
||||
// Mixed views can allocate either perspective. Choosing
|
||||
// a visible source must not navigate to its preferred view.
|
||||
self.selected = id;
|
||||
self.selected_attachment = None;
|
||||
self.refresh_draft();
|
||||
} else {
|
||||
self.select(id);
|
||||
}
|
||||
self.status = if session.candidates.is_empty() {
|
||||
"No compatible targets are visible. Expand containers or show a compatible endpoint in this view; Esc cancels drawing.".into()
|
||||
|
||||
@@ -89,10 +89,7 @@ async fn refused_click_keeps_field(mounted: &mut Mounted, f: &mut Fixture, field
|
||||
);
|
||||
assert!(!messages.iter().any(|m| matches!(
|
||||
m,
|
||||
Message::Select(_)
|
||||
| Message::CanvasSelect(..)
|
||||
| Message::SelectNode(_)
|
||||
| Message::MoveNode(..)
|
||||
Message::Select(_) | Message::SelectNode(_) | Message::MoveNode(..)
|
||||
)));
|
||||
assert_eq!(f.app.document.bundle(), &before);
|
||||
assert_eq!((f.app.selected, f.app.selected_node), selected);
|
||||
|
||||
@@ -237,10 +237,7 @@ fn mounted_dirty_property_press_does_not_select_apply_or_destroy_draft() {
|
||||
));
|
||||
assert!(!events.iter().any(|m| matches!(
|
||||
m,
|
||||
Message::Select(_)
|
||||
| Message::CanvasSelect(..)
|
||||
| Message::SelectNode(_)
|
||||
| Message::MoveNode(..)
|
||||
Message::Select(_) | Message::SelectNode(_) | Message::MoveNode(..)
|
||||
)));
|
||||
send(&mut f.app, events);
|
||||
assert_eq!((f.app.selected, f.app.selected_node), selection);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
//! Compact neutral tool palette. Tool state never changes its allocated width.
|
||||
use super::*;
|
||||
use crate::connection_tool::Tool;
|
||||
use iced::{
|
||||
Point, Rectangle, Size, Theme, mouse,
|
||||
widget::{
|
||||
@@ -12,7 +11,7 @@ use iced::{
|
||||
#[derive(Clone, Copy)]
|
||||
enum Icon {
|
||||
Element(Kind),
|
||||
Connection(Tool),
|
||||
Arrow,
|
||||
Existing,
|
||||
Select,
|
||||
Collapse(bool),
|
||||
@@ -77,59 +76,10 @@ impl canvas::Program<Message> for Icon {
|
||||
);
|
||||
}
|
||||
}
|
||||
Self::Connection(connection) => match connection {
|
||||
// The OA and SA work-flow tools share a visual family; they
|
||||
// never coexist in a palette. Keep their small glyph legible.
|
||||
Tool::Interaction | Tool::FunctionalExchange => {
|
||||
for x in [1., 11.] {
|
||||
f.stroke(
|
||||
&Path::rounded_rectangle(
|
||||
Point::new(x, 5.),
|
||||
Size::new(4., 6.),
|
||||
1.5.into(),
|
||||
),
|
||||
stroke,
|
||||
);
|
||||
}
|
||||
f.stroke(&line(&[(5., 8.), (10., 8.)]), stroke);
|
||||
f.stroke(&line(&[(7.5, 5.5), (10., 8.), (7.5, 10.5)]), stroke);
|
||||
}
|
||||
Tool::CommunicationMean => {
|
||||
// Participant endpoints, without implying a fixed direction.
|
||||
for x in [1., 11.] {
|
||||
f.stroke(
|
||||
&Path::rectangle(Point::new(x, 4.), Size::new(4., 8.)),
|
||||
stroke,
|
||||
);
|
||||
}
|
||||
f.stroke(&line(&[(5., 8.), (11., 8.)]), stroke);
|
||||
}
|
||||
Tool::BoundaryExchange => {
|
||||
// Ports on facing participant boundaries. Standard exchanges
|
||||
// are unoriented, so the generic tool has no arrowhead.
|
||||
for x in [2., 14.] {
|
||||
f.stroke(&line(&[(x, 2.), (x, 14.)]), stroke);
|
||||
}
|
||||
for x in [1., 12.] {
|
||||
f.stroke(
|
||||
&Path::rectangle(Point::new(x, 6.), Size::new(3., 4.)),
|
||||
stroke,
|
||||
);
|
||||
}
|
||||
f.stroke(&line(&[(4., 8.), (12., 8.)]), stroke);
|
||||
}
|
||||
Tool::Allocation => {
|
||||
// Work enters a performer: allocation can appear as nesting
|
||||
// rather than a relationship line in allocated-work views.
|
||||
f.stroke(&line(&[(1., 2.), (1., 14.), (15., 14.), (15., 2.)]), stroke);
|
||||
f.stroke(
|
||||
&Path::rounded_rectangle(Point::new(5., 9.), Size::new(6., 3.), 1.into()),
|
||||
stroke,
|
||||
);
|
||||
f.stroke(&line(&[(8., 1.), (8., 7.)]), stroke);
|
||||
f.stroke(&line(&[(5., 4.), (8., 7.), (11., 4.)]), stroke);
|
||||
}
|
||||
},
|
||||
Self::Arrow => {
|
||||
f.stroke(&line(&[(1., 8.), (14., 8.), (10., 4.)]), stroke);
|
||||
f.stroke(&line(&[(14., 8.), (10., 12.)]), stroke);
|
||||
}
|
||||
Self::Existing => {
|
||||
f.stroke(
|
||||
&Path::rectangle(Point::new(1., 1.), Size::new(10., 10.)),
|
||||
@@ -284,7 +234,7 @@ pub fn view(app: &App) -> Element<'_, Message> {
|
||||
}
|
||||
content = content.push(tool(
|
||||
app,
|
||||
Icon::Connection(*connection),
|
||||
Icon::Arrow,
|
||||
connection.label(),
|
||||
Message::Connection(crate::connection_edit::Event::Choose(Some(*connection))),
|
||||
app.connection
|
||||
|
||||
@@ -1,544 +0,0 @@
|
||||
//! Canvas inspection retains the displayed mixed-layer surface; navigation follows the model.
|
||||
use super::*;
|
||||
use crate::diagram_usage::Target;
|
||||
use crate::model_menu::{Event as MenuEvent, Request};
|
||||
|
||||
struct Fixture {
|
||||
app: App,
|
||||
diagram: Id,
|
||||
process: Id,
|
||||
placement: Id,
|
||||
edge: Id,
|
||||
}
|
||||
|
||||
fn fixture() -> Fixture {
|
||||
let mut p = Project::empty();
|
||||
let process = p
|
||||
.add_element(Kind::Process, Layer::Oa, "Operational process", None)
|
||||
.unwrap();
|
||||
let capability = p
|
||||
.add_element(Kind::Capability, Layer::Oa, "Operational capability", None)
|
||||
.unwrap();
|
||||
let edge = p.relate(
|
||||
RelationKind::ChainInvolvement,
|
||||
"Enables",
|
||||
process,
|
||||
capability,
|
||||
);
|
||||
p.ensure_diagram(Layer::Oa);
|
||||
let diagram = p.ensure_diagram(Layer::Sa);
|
||||
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Traceability;
|
||||
let placement = p.place(diagram, process, Position { x: 0., y: 0. });
|
||||
p.place(diagram, capability, Position { x: 500., y: 0. });
|
||||
let mut app = App::with_project(p);
|
||||
app.page = Page::Diagram;
|
||||
app.layer = Layer::Sa;
|
||||
app.active_diagram = Some(diagram);
|
||||
app.selected = diagram;
|
||||
app.refresh_draft();
|
||||
app.reconcile_multiselect();
|
||||
assert!(
|
||||
project_view(app.project(), diagram)
|
||||
.edges
|
||||
.iter()
|
||||
.any(|e| e.id == edge)
|
||||
);
|
||||
Fixture {
|
||||
app,
|
||||
diagram,
|
||||
process,
|
||||
placement,
|
||||
edge,
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_surface(f: &Fixture) {
|
||||
assert_eq!(f.app.page, Page::Diagram);
|
||||
assert_eq!(f.app.layer, Layer::Sa);
|
||||
assert_eq!(f.app.active_diagram, Some(f.diagram));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_projection_switch_restores_all_authored_content_and_one_step_history() {
|
||||
let mut f = fixture();
|
||||
let before = f.app.document.snapshot();
|
||||
let mut expected = before.decoded.project.clone();
|
||||
expected.diagrams.get_mut(&f.diagram).unwrap().projection = ProjectionKind::Allocation;
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::Projection(ProjectionKind::Allocation));
|
||||
assert_surface(&f);
|
||||
assert_eq!(f.app.project(), &expected);
|
||||
assert!(project_view(f.app.project(), f.diagram).nodes.is_empty());
|
||||
assert_eq!(f.app.project().diagrams[&f.diagram].nodes.len(), 2);
|
||||
let switched = f.app.document.snapshot();
|
||||
let _ = f.app.update(Message::Undo);
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &before.bundle);
|
||||
assert!(!f.app.document.can_undo());
|
||||
let _ = f.app.update(Message::Redo);
|
||||
assert_eq!(f.app.project(), &switched.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &switched.bundle);
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::Projection(ProjectionKind::Traceability));
|
||||
assert_surface(&f);
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert!(
|
||||
project_view(f.app.project(), f.diagram)
|
||||
.edges
|
||||
.iter()
|
||||
.any(|e| e.id == f.edge)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_canvas_edge_and_group_inspection_preserve_surface_and_history() {
|
||||
for grouped in [false, true] {
|
||||
let mut f = fixture();
|
||||
let before = f.app.document.snapshot();
|
||||
let revision = f.app.document.revision();
|
||||
if grouped {
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::DiagramSelection(multiselect::Event::Choose(
|
||||
f.app.multiselect_context().unwrap(),
|
||||
Target::Placement(f.placement),
|
||||
multiselect::Choice::Replace,
|
||||
)));
|
||||
}
|
||||
let message = if grouped {
|
||||
Message::DiagramSelection(multiselect::Event::Choose(
|
||||
f.app.multiselect_context().unwrap(),
|
||||
Target::Edge(f.edge),
|
||||
multiselect::Choice::Add,
|
||||
))
|
||||
} else {
|
||||
Message::CanvasSelect(f.app.route_context().unwrap(), f.edge)
|
||||
};
|
||||
let _ = f.app.update(message);
|
||||
assert_surface(&f);
|
||||
assert_eq!(f.app.selected, f.edge);
|
||||
assert_eq!(f.app.draft.as_ref().unwrap().original, f.edge);
|
||||
assert_eq!(
|
||||
f.app.multiselect_snapshot().unwrap().primary(),
|
||||
Some(&Target::Edge(f.edge))
|
||||
);
|
||||
assert_eq!(
|
||||
f.app.multiselect_snapshot().unwrap().targets().len(),
|
||||
if grouped { 2 } else { 1 }
|
||||
);
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &before.bundle);
|
||||
assert_eq!(f.app.document.revision(), revision);
|
||||
assert!(!f.app.document.can_undo());
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke(f: &mut Fixture, object: Id, placement: Option<Id>, action: Message) {
|
||||
let mut request = Request::object(object, iced::Point::ORIGIN);
|
||||
request.canvas = f.app.route_context();
|
||||
request.placement = placement;
|
||||
let _ = f.app.update(Message::Menu(MenuEvent::Open(request)));
|
||||
let token = f.app.model_menu.as_ref().unwrap().token;
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::Menu(MenuEvent::Invoke(token, Box::new(action))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_canvas_context_properties_and_hide_keep_the_captured_surface() {
|
||||
let mut f = fixture();
|
||||
let edge = f.edge;
|
||||
invoke(&mut f, edge, None, Message::EditProperties(edge));
|
||||
assert_surface(&f);
|
||||
assert_eq!(f.app.selected, edge);
|
||||
assert!(!f.app.document.can_undo());
|
||||
let before = f.app.document.snapshot();
|
||||
let process = f.process;
|
||||
let placement = f.placement;
|
||||
invoke(&mut f, process, Some(placement), Message::HideSelected);
|
||||
assert_surface(&f);
|
||||
assert!(
|
||||
!f.app.project().diagrams[&f.diagram]
|
||||
.nodes
|
||||
.contains_key(&placement)
|
||||
);
|
||||
assert!(f.app.project().elements.contains_key(&process));
|
||||
let _ = f.app.update(Message::Undo);
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &before.bundle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_canvas_explicit_navigation_still_follows_the_target_layer() {
|
||||
let mut f = fixture();
|
||||
let process = f.process;
|
||||
let edge = f.edge;
|
||||
invoke(&mut f, edge, None, Message::Select(process));
|
||||
assert_eq!(f.app.selected, process);
|
||||
assert_eq!(f.app.layer, Layer::Oa);
|
||||
assert_ne!(f.app.active_diagram, Some(f.diagram));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_navigation_to_the_inspected_cross_layer_object_follows_its_layer() {
|
||||
let mut f = fixture();
|
||||
let _ = f.app.update(Message::CanvasSelect(
|
||||
f.app.route_context().unwrap(),
|
||||
f.edge,
|
||||
));
|
||||
assert_surface(&f);
|
||||
assert_eq!(f.app.selected, f.edge);
|
||||
let before = f.app.document.snapshot();
|
||||
let _ = f.app.update(Message::Select(f.edge));
|
||||
assert_eq!(f.app.selected, f.edge);
|
||||
assert_eq!(f.app.layer, Layer::Oa);
|
||||
assert_ne!(f.app.active_diagram, Some(f.diagram));
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &before.bundle);
|
||||
assert!(!f.app.document.can_undo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_canvas_invalid_draft_and_stale_packets_preserve_selection() {
|
||||
let mut f = fixture();
|
||||
let context = f.app.route_context().unwrap();
|
||||
let _ = f.app.update(Message::Field("/id".into(), "-".into()));
|
||||
let draft = serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot()).unwrap();
|
||||
let _ = f.app.update(Message::CanvasSelect(context, f.edge));
|
||||
assert_eq!(f.app.selected, f.diagram);
|
||||
assert_surface(&f);
|
||||
assert_eq!(
|
||||
serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot()).unwrap(),
|
||||
draft
|
||||
);
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::DiagramSelection(multiselect::Event::Choose(
|
||||
f.app.multiselect_context().unwrap(),
|
||||
Target::Edge(f.edge),
|
||||
multiselect::Choice::Replace,
|
||||
)));
|
||||
assert_eq!(f.app.selected, f.diagram);
|
||||
assert_eq!(
|
||||
serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot()).unwrap(),
|
||||
draft
|
||||
);
|
||||
let _ = f.app.update(Message::RevertProperties);
|
||||
f.app.canvas_epoch += 1;
|
||||
let _ = f.app.update(Message::CanvasSelect(context, f.edge));
|
||||
assert_eq!(f.app.selected, f.diagram);
|
||||
assert!(!f.app.document.can_undo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cross_layer_port_inspection_and_connection_start_retain_the_view() {
|
||||
let mut p = Project::empty();
|
||||
let owner = p
|
||||
.add_element(Kind::Function, Layer::Sa, "Send", None)
|
||||
.unwrap();
|
||||
let port = Id::new_v4();
|
||||
let element = make_port(
|
||||
&p,
|
||||
port,
|
||||
owner,
|
||||
"Output",
|
||||
PortContract::FunctionOutput {
|
||||
outgoing_items: vec![],
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
p.elements.insert(port, element);
|
||||
p.ensure_diagram(Layer::Sa);
|
||||
let diagram = p.ensure_diagram(Layer::Oa);
|
||||
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Traceability;
|
||||
let placement = p.place(diagram, owner, Position { x: 0., y: 0. });
|
||||
let mut app = App::with_project(p);
|
||||
app.page = Page::Diagram;
|
||||
app.layer = Layer::Oa;
|
||||
app.active_diagram = Some(diagram);
|
||||
app.selected = diagram;
|
||||
app.refresh_draft();
|
||||
app.reconcile_multiselect();
|
||||
assert!(
|
||||
project_view(app.project(), diagram)
|
||||
.ports
|
||||
.iter()
|
||||
.any(|p| p.element == port)
|
||||
);
|
||||
let _ = app.update(Message::CanvasSelect(app.route_context().unwrap(), port));
|
||||
assert_eq!(app.selected, port);
|
||||
assert_eq!(app.active_diagram, Some(diagram));
|
||||
assert_eq!(app.layer, Layer::Oa);
|
||||
assert_eq!(
|
||||
app.multiselect_snapshot().unwrap().primary(),
|
||||
Some(&Target::Port { port, placement })
|
||||
);
|
||||
// Reset inspection so the endpoint path cannot pass by selecting the same ID.
|
||||
app.selected = diagram;
|
||||
app.refresh_draft();
|
||||
let context = app.route_context().unwrap();
|
||||
// Mixed canvases can retain a tool from either perspective.
|
||||
app.connection = Some(crate::connection_edit::Session::new(
|
||||
app.project(),
|
||||
&project_view(app.project(), diagram),
|
||||
context,
|
||||
crate::connection_tool::Tool::FunctionalExchange,
|
||||
));
|
||||
let _ = app.update(Message::Connection(
|
||||
crate::connection_edit::Event::Endpoint(context, port),
|
||||
));
|
||||
assert_eq!(app.selected, port);
|
||||
assert_eq!(app.layer, Layer::Oa);
|
||||
assert_eq!(app.active_diagram, Some(diagram));
|
||||
assert_eq!(app.connection.as_ref().unwrap().source, Some(port));
|
||||
assert!(!app.document.can_undo());
|
||||
}
|
||||
|
||||
fn authoring_fixture(performer: bool) -> (Fixture, Id) {
|
||||
let mut f = fixture();
|
||||
let mut project = f.app.project().clone();
|
||||
let owner = project
|
||||
.add_element(
|
||||
if performer {
|
||||
Kind::Entity
|
||||
} else {
|
||||
Kind::Activity
|
||||
},
|
||||
Layer::Oa,
|
||||
"Operational owner",
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let target = project
|
||||
.add_element(Kind::Activity, Layer::Oa, "Operational target", None)
|
||||
.unwrap();
|
||||
let placement = project.place(f.diagram, owner, Position { x: 0., y: 400. });
|
||||
project.place(f.diagram, target, Position { x: 500., y: 400. });
|
||||
f.app = App::with_project(project);
|
||||
f.app.page = Page::Diagram;
|
||||
f.app.layer = Layer::Sa;
|
||||
f.app.active_diagram = Some(f.diagram);
|
||||
f.app.selected = f.diagram;
|
||||
f.app.refresh_draft();
|
||||
f.app.reconcile_multiselect();
|
||||
f.process = owner;
|
||||
f.placement = placement;
|
||||
(f, target)
|
||||
}
|
||||
|
||||
fn assert_source_pair(app: &App) {
|
||||
let compiled = oa_core::source_project::compile_bundle(
|
||||
app.document.bundle(),
|
||||
oa_core::source_project::ProjectLimits::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(&compiled.decoded.project, app.project());
|
||||
assert_eq!(&compiled.bundle, app.document.bundle());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_canvas_relationship_actions_keep_operational_source_and_commit_one_step() {
|
||||
for context_action in [false, true] {
|
||||
let (mut f, target) = authoring_fixture(false);
|
||||
let before = f.app.document.snapshot();
|
||||
let owner = f.process;
|
||||
let placement = f.placement;
|
||||
if context_action {
|
||||
invoke(
|
||||
&mut f,
|
||||
owner,
|
||||
Some(placement),
|
||||
Message::AddRelationKind(RelationKind::Interaction, false),
|
||||
);
|
||||
} else {
|
||||
let _ = f.app.update(Message::SelectNode(placement));
|
||||
let _ = f.app.update(Message::AddRelation);
|
||||
}
|
||||
assert_surface(&f);
|
||||
let Some(crate::workflow::Workflow::Relation(relation)) = &f.app.workflow else {
|
||||
panic!("relationship form did not open: {}", f.app.status);
|
||||
};
|
||||
assert_eq!(
|
||||
relation.source.as_ref().map(|source| source.id),
|
||||
Some(owner)
|
||||
);
|
||||
assert_eq!(relation.kind, RelationKind::Interaction);
|
||||
let saved = f
|
||||
.app
|
||||
.workflow
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.recovery_snapshot()
|
||||
.unwrap();
|
||||
assert_eq!(saved["Relation"]["layer"], "Oa");
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::Workflow(crate::workflow::Event::Target(Choice {
|
||||
id: target,
|
||||
label: "Operational target".into(),
|
||||
})));
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::Workflow(crate::workflow::Event::Apply));
|
||||
assert!(f.app.workflow.is_none(), "{}", f.app.status);
|
||||
let created: Vec<_> = f
|
||||
.app
|
||||
.project()
|
||||
.relations
|
||||
.values()
|
||||
.filter(|relation| !before.decoded.project.relations.contains_key(&relation.id))
|
||||
.collect();
|
||||
assert_eq!(created.len(), 1);
|
||||
assert_eq!(
|
||||
(created[0].kind(), created[0].source(), created[0].target()),
|
||||
(RelationKind::Interaction, owner, target)
|
||||
);
|
||||
assert_source_pair(&f.app);
|
||||
let _ = f.app.update(Message::Undo);
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &before.bundle);
|
||||
assert!(!f.app.document.can_undo());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_canvas_add_child_command_uses_parent_layer_for_containment_and_allocation() {
|
||||
for performer in [false, true] {
|
||||
let (mut f, _) = authoring_fixture(performer);
|
||||
let before = f.app.document.snapshot();
|
||||
let owner = f.process;
|
||||
let placement = f.placement;
|
||||
invoke(
|
||||
&mut f,
|
||||
owner,
|
||||
Some(placement),
|
||||
Message::CreateContextElement(Kind::Activity, true),
|
||||
);
|
||||
assert_surface(&f);
|
||||
let created: Vec<_> = f
|
||||
.app
|
||||
.project()
|
||||
.elements
|
||||
.values()
|
||||
.filter(|element| !before.decoded.project.elements.contains_key(&element.id))
|
||||
.collect();
|
||||
assert_eq!(created.len(), 1, "{}", f.app.status);
|
||||
let child = created[0].id;
|
||||
assert_eq!(created[0].kind(), Kind::Activity);
|
||||
assert_eq!(created[0].layer(), Layer::Oa);
|
||||
assert_eq!(
|
||||
created[0].parent,
|
||||
if performer { None } else { Some(owner) }
|
||||
);
|
||||
let allocations: Vec<_> = f
|
||||
.app
|
||||
.project()
|
||||
.relations
|
||||
.values()
|
||||
.filter(|relation| !before.decoded.project.relations.contains_key(&relation.id))
|
||||
.collect();
|
||||
assert_eq!(allocations.len(), usize::from(performer));
|
||||
if performer {
|
||||
assert_eq!(
|
||||
(
|
||||
allocations[0].kind(),
|
||||
allocations[0].source(),
|
||||
allocations[0].target()
|
||||
),
|
||||
(RelationKind::Allocation, owner, child)
|
||||
);
|
||||
}
|
||||
assert!(
|
||||
f.app.project().diagrams[&f.diagram]
|
||||
.nodes
|
||||
.values()
|
||||
.any(|node| node.element == child)
|
||||
);
|
||||
assert_source_pair(&f.app);
|
||||
let _ = f.app.update(Message::Undo);
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &before.bundle);
|
||||
assert!(!f.app.document.can_undo());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mixed_canvas_root_creation_still_uses_the_active_perspective() {
|
||||
let (mut f, _) = authoring_fixture(false);
|
||||
let _ = f.app.update(Message::SelectNode(f.placement));
|
||||
let before = f.app.document.snapshot();
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::CreateContextElement(Kind::Function, false));
|
||||
assert_surface(&f);
|
||||
let created: Vec<_> = f
|
||||
.app
|
||||
.project()
|
||||
.elements
|
||||
.values()
|
||||
.filter(|element| !before.decoded.project.elements.contains_key(&element.id))
|
||||
.collect();
|
||||
assert_eq!(created.len(), 1, "{}", f.app.status);
|
||||
assert_eq!(created[0].kind(), Kind::Function);
|
||||
assert_eq!(created[0].layer(), Layer::Sa);
|
||||
assert_eq!(created[0].parent, None);
|
||||
assert_source_pair(&f.app);
|
||||
let _ = f.app.update(Message::Undo);
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &before.bundle);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_creation_navigates_to_a_parent_layer_preferred_view_only_when_needed() {
|
||||
for existing_preferred in [false, true] {
|
||||
let (mut f, _) = authoring_fixture(false);
|
||||
let mut project = f.app.project().clone();
|
||||
for diagram in project
|
||||
.diagrams
|
||||
.values_mut()
|
||||
.filter(|d| d.layer == Layer::Oa)
|
||||
{
|
||||
diagram.projection = if existing_preferred {
|
||||
ProjectionKind::Allocation
|
||||
} else {
|
||||
ProjectionKind::Overview
|
||||
};
|
||||
}
|
||||
project.diagrams.get_mut(&f.diagram).unwrap().projection = ProjectionKind::Capabilities;
|
||||
f.app = App::with_project(project);
|
||||
let _ = f.app.update(Message::Select(f.process));
|
||||
let _ = f.app.update(Message::Diagram(Choice {
|
||||
id: f.diagram,
|
||||
label: "System capabilities".into(),
|
||||
}));
|
||||
assert_eq!(f.app.selected, f.process);
|
||||
assert_eq!(f.app.layer, Layer::Sa);
|
||||
assert_eq!(f.app.active_diagram, Some(f.diagram));
|
||||
let before = f.app.document.snapshot();
|
||||
let _ = f
|
||||
.app
|
||||
.update(Message::CreateContextElement(Kind::Activity, true));
|
||||
assert_eq!(f.app.layer, Layer::Oa, "{}", f.app.status);
|
||||
let diagram = f.app.active_diagram.unwrap();
|
||||
assert_ne!(diagram, f.diagram);
|
||||
assert_eq!(f.app.project().diagrams[&diagram].layer, Layer::Oa);
|
||||
assert_eq!(
|
||||
f.app.project().diagrams[&diagram].projection,
|
||||
ProjectionKind::Allocation
|
||||
);
|
||||
assert_eq!(
|
||||
f.app.project().diagrams.len(),
|
||||
before.decoded.project.diagrams.len() + usize::from(!existing_preferred)
|
||||
);
|
||||
assert_eq!(
|
||||
f.app.project().elements[&f.app.selected].parent,
|
||||
Some(f.process)
|
||||
);
|
||||
assert_source_pair(&f.app);
|
||||
let _ = f.app.update(Message::Undo);
|
||||
assert_eq!(f.app.project(), &before.decoded.project);
|
||||
assert_eq!(f.app.document.bundle(), &before.bundle);
|
||||
}
|
||||
}
|
||||
@@ -993,10 +993,7 @@ impl canvas::Program<Message> for DiagramCanvas<'_> {
|
||||
world,
|
||||
));
|
||||
}
|
||||
return Some(
|
||||
Action::publish(Message::CanvasSelect(self.context, port.element))
|
||||
.and_capture(),
|
||||
);
|
||||
return Some(Action::publish(Message::Select(port.element)).and_capture());
|
||||
}
|
||||
if let Some(id) = self.hit(view, p) {
|
||||
let position = self
|
||||
@@ -1013,7 +1010,7 @@ impl canvas::Program<Message> for DiagramCanvas<'_> {
|
||||
.hit_label(&scene.routed, p)
|
||||
.or_else(|| self.hit_association_label(&scene.routed, p))
|
||||
{
|
||||
Some(Action::publish(Message::CanvasSelect(self.context, id)).and_capture())
|
||||
Some(Action::publish(Message::Select(id)).and_capture())
|
||||
} else if let Some((id, segment)) = self.hit_edge(&scene.routed, p) {
|
||||
if id == self.selected
|
||||
&& self.editable
|
||||
@@ -1029,7 +1026,7 @@ impl canvas::Program<Message> for DiagramCanvas<'_> {
|
||||
self.begin_route(state, world, Handle::Bend(index), route);
|
||||
Some(Action::capture())
|
||||
} else {
|
||||
Some(Action::publish(Message::CanvasSelect(self.context, id)).and_capture())
|
||||
Some(Action::publish(Message::Select(id)).and_capture())
|
||||
}
|
||||
} else {
|
||||
state.pan_start = Some((mouse::Button::Left, p));
|
||||
@@ -2444,7 +2441,7 @@ mod tests {
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.0;
|
||||
assert!(matches!(selected,Some(Message::CanvasSelect(_, id)) if id==port.element));
|
||||
assert!(matches!(selected,Some(Message::Select(id)) if id==port.element));
|
||||
let request = canvas.context_request(
|
||||
&state,
|
||||
click,
|
||||
@@ -3057,7 +3054,7 @@ mod tests {
|
||||
let mut state = State::default();
|
||||
assert_eq!(canvas.hit_association_label(&routed, at), Some(edge));
|
||||
assert!(
|
||||
matches!(pointer(&canvas,&mut state,mouse::Event::ButtonPressed(mouse::Button::Left),at),Some(Message::CanvasSelect(_, id)) if id==edge)
|
||||
matches!(pointer(&canvas,&mut state,mouse::Event::ButtonPressed(mouse::Button::Left),at),Some(Message::Select(id)) if id==edge)
|
||||
);
|
||||
assert!(
|
||||
state.route_drag.is_none(),
|
||||
@@ -3107,7 +3104,7 @@ mod tests {
|
||||
let center = scene.routed.edges[&edge].label.center;
|
||||
let screen = point(center) + canvas.pan;
|
||||
assert!(
|
||||
matches!(pointer(&canvas,&mut state,mouse::Event::ButtonPressed(mouse::Button::Left),screen),Some(Message::CanvasSelect(_, id)) if id==edge)
|
||||
matches!(pointer(&canvas,&mut state,mouse::Event::ButtonPressed(mouse::Button::Left),screen),Some(Message::Select(id)) if id==edge)
|
||||
);
|
||||
canvas.selected = edge;
|
||||
pointer(
|
||||
|
||||
@@ -179,7 +179,7 @@ fn coalesced_port_drag_commits_once_through_app_and_source_history() {
|
||||
zoom,
|
||||
);
|
||||
assert_eq!(messages.len(), 2);
|
||||
assert!(matches!(&messages[0],Message::CanvasSelect(_, id)if *id==output));
|
||||
assert!(matches!(&messages[0],Message::Select(id)if *id==output));
|
||||
assert!(
|
||||
matches!(&messages[1],Message::PortAnchor(port_edit::Event::Commit(_,_,anchor))if anchor.port==output&&anchor.side==PortAnchorSide::Top)
|
||||
);
|
||||
@@ -251,7 +251,7 @@ fn menu_captured_motion_must_not_turn_following_click_into_old_port_drag() {
|
||||
b,
|
||||
);
|
||||
let picked = match &press {
|
||||
Message::CanvasSelect(_, id) => Some(*id),
|
||||
Message::Select(id) => Some(*id),
|
||||
_ => None,
|
||||
};
|
||||
let release_debug = format!("{release:?}");
|
||||
@@ -300,7 +300,7 @@ fn drag_from_already_selected_stationary_port_keeps_source_between_batches() {
|
||||
.filter_map(|e| event(&c, &mut state, e, b))
|
||||
.collect();
|
||||
assert!(
|
||||
matches!(messages.first(),Some(Message::CanvasSelect(_, id)) if *id==output),
|
||||
matches!(messages.first(),Some(Message::Select(id)) if *id==output),
|
||||
"Stationary source was lost between batches: {messages:?}"
|
||||
);
|
||||
assert!(
|
||||
|
||||
@@ -165,7 +165,7 @@ fn nested_owner_palette_selection_drag_and_context_menu_pick_the_same_port() {
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.0;
|
||||
assert!(matches!(press, Some(Message::CanvasSelect(_, id)) if id == port.element));
|
||||
assert!(matches!(press, Some(Message::Select(id)) if id == port.element));
|
||||
assert_eq!(state.port_drag.as_ref().unwrap().anchor.port, port.element);
|
||||
let menu = canvas.context_request(&state, at, bounds);
|
||||
assert_eq!(menu.object, port.element);
|
||||
|
||||
@@ -375,7 +375,7 @@ mod tests {
|
||||
messages.extend(message);
|
||||
}
|
||||
}
|
||||
assert!(matches!(messages.first(), Some(Message::CanvasSelect(_, id)) if *id == port));
|
||||
assert!(matches!(messages.first(), Some(Message::Select(id)) if *id == port));
|
||||
assert!(
|
||||
matches!(messages.last(), Some(Message::PortAnchor(crate::port_edit::Event::Commit(_, _, anchor))) if anchor.port == port && anchor.side == PortAnchorSide::Top)
|
||||
);
|
||||
|
||||
@@ -322,7 +322,6 @@ fn article(topic: Topic) -> Article {
|
||||
"Show a correspondence between OA and SA",
|
||||
&[
|
||||
"Choose OA ↔ SA traceability in View content. Changing this setting does not create counterparts or add their placements, so an existing single-layer diagram can initially look unchanged.",
|
||||
"Switching back to Architecture or another single-layer projection shows only that view's layer. The other layer's placements, routing and saved visibility remain stored; return to OA ↔ SA traceability to show them again, subject to the view's filters. Selecting a displayed card, port or line and opening its Properties keeps the current view and perspective.",
|
||||
"Use Review layer traces to inspect named Operational and System endpoints and the meaning of their link. This opens the model's layer-trace list with no search restriction; traces between relationships are reviewed here rather than drawn between element cards.",
|
||||
"In the chosen architecture view, use Add existing elements. The chooser offers compatible elements from both layers. Select an existing endpoint and click its position in the canvas; repeat for the other end if needed. This adds a placement, not a new semantic element or trace. For port correspondences, place the ports' owners.",
|
||||
"The view shows correspondence lines alongside ordinary relationships. The shown-line count reflects the current projection and visibility settings, not trace coverage. If a line is hidden, use View visibility and inspect Saved view visibility; placing an endpoint does not remove explicit hides or filters.",
|
||||
|
||||
@@ -372,150 +372,3 @@ async fn source6_http_explicit_communication_ports_preview_apply_and_restart_pre
|
||||
assert_pair(&reopened);
|
||||
server.stop().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn mixed_traceability_projection_roundtrip_preserves_dormant_http_state() {
|
||||
let f = fixture();
|
||||
let mut model = compile_bundle(&f.bundle, ProjectLimits::default())
|
||||
.unwrap()
|
||||
.decoded
|
||||
.project;
|
||||
let view = model.diagrams.get_mut(&f.diagram).unwrap();
|
||||
view.layer = Layer::Sa;
|
||||
view.projection = ProjectionKind::Traceability;
|
||||
// The OA placements and category are intentionally opposite the view's layer.
|
||||
view.view_options.hidden_edges = vec![f.mean];
|
||||
for (index, element) in f.sa.into_iter().enumerate() {
|
||||
model.place(
|
||||
f.diagram,
|
||||
element,
|
||||
Position {
|
||||
x: index as f32 * 400.,
|
||||
y: 300.,
|
||||
},
|
||||
);
|
||||
}
|
||||
let mut bundle = f.bundle.clone();
|
||||
bundle.files.insert(
|
||||
bundle.entry.clone(),
|
||||
format!(
|
||||
"// Authored source-6 engineering rationale\n{}",
|
||||
oa_core::language::format(&model).replacen(
|
||||
"{\n",
|
||||
"{\n include \"notes.arcadia\";\n",
|
||||
1
|
||||
)
|
||||
),
|
||||
);
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let server = TestServer::start(directory.path(), None).await;
|
||||
let original = import(&server, &bundle).await;
|
||||
assert_pair(&original);
|
||||
let resource = original.resource_id;
|
||||
let mixed = oa_core::projection::project_view(&original.model, f.diagram);
|
||||
assert!(
|
||||
f.oa.iter()
|
||||
.chain(&f.sa)
|
||||
.all(|id| mixed.nodes.iter().any(|n| n.element == *id))
|
||||
);
|
||||
|
||||
let mut ordinary = original.model.diagrams[&f.diagram].clone();
|
||||
ordinary.projection = ProjectionKind::Allocation;
|
||||
let response = transact(
|
||||
&server,
|
||||
&original,
|
||||
vec![Command::EditDiagram(f.diagram, ordinary.clone())],
|
||||
)
|
||||
.await;
|
||||
let status = response.status();
|
||||
let etag = response
|
||||
.headers()
|
||||
.get("etag")
|
||||
.map(|v| v.to_str().unwrap().to_owned());
|
||||
let result: Value = response.json().await.unwrap();
|
||||
assert_eq!(status, StatusCode::OK, "{result}");
|
||||
assert_eq!(result["changed"], true);
|
||||
assert_eq!(result["dry_run"], false);
|
||||
assert_eq!(result["revision"], original.revision + 1);
|
||||
let changed = server.get(resource).await;
|
||||
assert_eq!(etag.as_deref(), Some(changed.etag().as_str()));
|
||||
let mut expected = original.model.clone();
|
||||
expected.diagrams.insert(f.diagram, ordinary.clone());
|
||||
assert_eq!(
|
||||
changed.model, expected,
|
||||
"only the projection changes; dormant state is retained"
|
||||
);
|
||||
assert_pair(&changed);
|
||||
assert_eq!(
|
||||
changed.source_project.files["notes.arcadia"],
|
||||
bundle.files["notes.arcadia"]
|
||||
);
|
||||
assert!(
|
||||
changed.source_project.files[&bundle.entry]
|
||||
.starts_with("// Authored source-6 engineering rationale\n")
|
||||
);
|
||||
let projected = oa_core::projection::project_view(&changed.model, f.diagram);
|
||||
assert!(projected.nodes.iter().all(|n| !f.oa.contains(&n.element)));
|
||||
assert!(
|
||||
f.sa.iter()
|
||||
.all(|id| projected.nodes.iter().any(|n| n.element == *id))
|
||||
);
|
||||
|
||||
// A dormant option still must name an existing category. Rejection must not
|
||||
// advance the ETag or rewrite either side of the persisted model/source pair.
|
||||
let disk = directory.path().join(format!("{resource}.json"));
|
||||
let accepted_bytes = std::fs::read(&disk).unwrap();
|
||||
ordinary.view_options.participant_categories = CategorySelection::Any(vec![Id::new_v4()]);
|
||||
let rejected = transact(
|
||||
&server,
|
||||
&changed,
|
||||
vec![Command::EditDiagram(f.diagram, ordinary)],
|
||||
)
|
||||
.await;
|
||||
assert_eq!(rejected.status(), StatusCode::UNPROCESSABLE_ENTITY);
|
||||
let retained = server.get(resource).await;
|
||||
assert_eq!(retained.etag(), changed.etag());
|
||||
assert_eq!(
|
||||
serde_json::to_value(&retained).unwrap(),
|
||||
serde_json::to_value(&changed).unwrap()
|
||||
);
|
||||
assert_eq!(std::fs::read(&disk).unwrap(), accepted_bytes);
|
||||
server.stop().await;
|
||||
|
||||
let server = TestServer::start(directory.path(), None).await;
|
||||
let reopened = server.get(resource).await;
|
||||
assert_eq!(
|
||||
serde_json::to_value(&reopened).unwrap(),
|
||||
serde_json::to_value(&changed).unwrap()
|
||||
);
|
||||
assert_pair(&reopened);
|
||||
let response = transact(
|
||||
&server,
|
||||
&reopened,
|
||||
vec![Command::EditDiagram(
|
||||
f.diagram,
|
||||
original.model.diagrams[&f.diagram].clone(),
|
||||
)],
|
||||
)
|
||||
.await;
|
||||
let status = response.status();
|
||||
let result: Value = response.json().await.unwrap();
|
||||
assert_eq!(status, StatusCode::OK, "{result}");
|
||||
assert_eq!(result["changed"], true);
|
||||
assert_eq!(result["revision"], changed.revision + 1);
|
||||
let restored = server.get(resource).await;
|
||||
assert_eq!(restored.model, original.model);
|
||||
assert_pair(&restored);
|
||||
assert_eq!(
|
||||
restored.source_project.files["notes.arcadia"],
|
||||
bundle.files["notes.arcadia"]
|
||||
);
|
||||
let projected = oa_core::projection::project_view(&restored.model, f.diagram);
|
||||
assert!(
|
||||
f.oa.iter()
|
||||
.chain(&f.sa)
|
||||
.all(|id| projected.nodes.iter().any(|n| n.element == *id))
|
||||
);
|
||||
assert!(!projected.edges.iter().any(|edge| edge.id == f.mean));
|
||||
server.stop().await;
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ schemas["ViewOptions"] = obj({
|
||||
"coverage": {**ref("Coverage"), "default": "All"},
|
||||
"work_contexts": {**ref("ContextSelection"), "default": "All"},
|
||||
"exchange_labels": {**ref("ExchangeLabels"), "default": "Name"}}, [])
|
||||
schemas["ViewOptions"]["description"] = "Presentation only. Independent predicates intersect; category members union within a family. Only([]) hides all relations and traces. Any([]) remains an explicit empty selection. String projections preserve both-layer hidden/category/context references and latent placements; only Traceability renders both layers. Typed StateMachine/Scenario views retain layer-scoped admission. Filtering preserves unfiltered automatic port positions."
|
||||
schemas["ViewOptions"]["description"] = "Presentation only. Independent predicates intersect; category members union within a family. Only([]) hides all relations and traces. Any([]) remains an explicit empty selection. Traceability permits both-layer hidden/category/context references; ordinary projections require the diagram layer. Filtering preserves unfiltered automatic port positions."
|
||||
schemas["Diagram"]["properties"]["view_options"] = {**ref("ViewOptions"), "default": {}}
|
||||
|
||||
def upsert_variant(variants, name, value):
|
||||
|
||||
+2
-9
@@ -570,15 +570,8 @@ retain their identities and contracts; migration does not silently replace them.
|
||||
``coverage`` and ``work_contexts``, and ``Name`` for ``exchange_labels``.
|
||||
``{"Only":[]}`` hides all relation and trace lines; ``{"Any":[]}`` preserves
|
||||
an explicit empty category/context selection. Category members union within
|
||||
each family, and independent filters intersect. String projections permit saved
|
||||
references and placements from both layers. Only ``Traceability`` renders both;
|
||||
other string projections filter to the diagram's declared layer. ``EditDiagram``
|
||||
may change between these projections without deleting latent placements, routes
|
||||
or visibility choices. Returning to Traceability restores their applicability.
|
||||
Typed StateMachine/Scenario views retain their strict membership and layer checks.
|
||||
Missing objects, wrong reference kinds/families and duplicate selections remain
|
||||
invalid. Frozen versions 1–6 keep their prior layer admission before migration;
|
||||
older version-7 implementations may reject these newly accepted ordinary views.
|
||||
each family, and independent filters intersect. ``Traceability`` permits
|
||||
references from both layers; ordinary projections require the diagram's layer.
|
||||
These presentation choices preserve semantic records, routes, placements and
|
||||
the automatic port positions derived from unfiltered eligible exchanges.
|
||||
|
||||
|
||||
+2
-20
@@ -409,12 +409,8 @@ filters rather than widening them to All.
|
||||
|
||||
Traceability views retain their established two-layer projection: default options
|
||||
preserve eligible OA and SA lines. Their hidden-edge, category and context
|
||||
references may therefore select either layer. Ordinary string projections retain
|
||||
these references from either layer as saved presentation choices; they still
|
||||
render only their declared layer. Switching projections MUST preserve those
|
||||
choices, including explicit empty selections. Existing-object, reference-kind,
|
||||
family and uniqueness constraints still apply. Typed StateMachine and Scenario
|
||||
views retain layer-scoped reference admission. A filter never hides the other
|
||||
references may therefore select either layer. All ordinary projections require
|
||||
these references in the declared diagram layer. A filter never hides the other
|
||||
layer merely because the Traceability diagram has an OA or SA layer label.
|
||||
|
||||
Versions 1–5 reject the new relation, category constructor, diagram field (even
|
||||
@@ -1077,20 +1073,6 @@ capabilities with their involvement, exploitation, include, extend and
|
||||
generalization links. Decomposition and Overview retain general containment;
|
||||
Overview also exposes all relationships. These string projections hide
|
||||
inapplicable placements without deleting them or changing semantic records.
|
||||
This includes opposite-layer placements retained after leaving Traceability.
|
||||
Changing between string projections changes only the projection value; placement
|
||||
identities, positions, sizes, collapsed state, port anchors, routes and visibility
|
||||
choices remain authored. Returning to Traceability restores their applicability.
|
||||
Ordinary views MUST NOT render opposite-layer nodes, parents, ports or lines.
|
||||
This current-version admission correction does not broaden frozen source/JSON
|
||||
versions 1–6: their layer-scoped placement and visibility checks remain required
|
||||
before migration. Earlier version-7 implementations can reject ordinary views
|
||||
containing these retained opposite-layer records.
|
||||
|
||||
The explicit RelocateNode operation moves a placement to a different diagram
|
||||
only when its element belongs to the destination layer or the destination is
|
||||
Traceability. Retaining an existing hidden placement through a projection change
|
||||
does not authorize a relocation that immediately hides it in another layer.
|
||||
|
||||
A StateMachine projection MUST reference an existing StateMachine in the
|
||||
diagram's layer. Its placements MUST refer to that machine, its Regions, or
|
||||
|
||||
+1
-1
@@ -12490,7 +12490,7 @@
|
||||
},
|
||||
"required": [],
|
||||
"additionalProperties": false,
|
||||
"description": "Presentation only. Independent predicates intersect; category members union within a family. Only([]) hides all relations and traces. Any([]) remains an explicit empty selection. String projections preserve both-layer hidden/category/context references and latent placements; only Traceability renders both layers. Typed StateMachine/Scenario views retain layer-scoped admission. Filtering preserves unfiltered automatic port positions."
|
||||
"description": "Presentation only. Independent predicates intersect; category members union within a family. Only([]) hides all relations and traces. Any([]) remains an explicit empty selection. Traceability permits both-layer hidden/category/context references; ordinary projections require the diagram layer. Filtering preserves unfiltered automatic port positions."
|
||||
},
|
||||
"CommunicationPortChoice": {
|
||||
"oneOf": [
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# Connection tool icons
|
||||
|
||||
Branch candidate for [#41](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/41), based on published Windows preview source `715036e132677dd2bc2fb37ed316d93dc9987aec`. This is not integrated or native acceptance.
|
||||
|
||||
The reported collapsed palette repeats one arrow for three different tools. Extend the existing Rust/Iced canvas glyphs at their actual 16×16 size: work endpoints plus an arrow for Interaction/Functional exchange, neutral participant endpoints for Communication means, facing component ports for Boundary exchange, and work entering a performer for Allocation. The first pair never coexist in one perspective. Unoriented generic tools must not imply a fixed direction.
|
||||
|
||||
Letter badges were rejected as language-dependent and cramped. Different arrow/dash styles risk confusing tool identity with relation direction. Native endpoint/container shapes fit the existing icon system without font or asset dependencies. Preserve button dimensions, explicit labels/tooltips, ordering, messages, availability and selection rules.
|
||||
|
||||
Independent source critique found no blocker and confirmed the tool semantics and Iced API use. Actual-size legibility remains unverified, particularly the exchange arrow, port details and possible download/import interpretation of Allocation. Verify enabled, disabled, hovered and selected palettes in both perspectives, expansion/collapse, tool activation, representative relation creation and cancellation. Existing keyboard/accessibility behavior must be checked, not inferred from hover labels. Reuse semantic connection tests; geometry hashes alone are not an identification or usability evaluator.
|
||||
|
||||
Linux remains excluded by the existing user pause. Live ownership and integration status belong to the linked issue.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Independent downloaded Windows package QA
|
||||
|
||||
Codex-9e8r6t /root, 2026-09-24. **PASS for the bounded package, State presentation/picker/reopen, built-in guide and packaged-server checks below.** No material blocker found in this scope. This evidence supports the release owner's admission decision; it does not claim completion of issue 37, the full checklist, or the user application goal.
|
||||
|
||||
## Exact artifact and source
|
||||
|
||||
- Draft release 7: `v0.2.0-preview.integrated.20260924`, source **`715036e132677dd2bc2fb37ed316d93dc9987aec`**.
|
||||
- Studio asset 25: `ArcadiaStudio-0.2.0-preview.integrated.20260924-x86_64-pc-windows-gnullvm.zip`, 34,168,661 bytes, SHA256 **`5554e32533e3cb2c33fb4143a5a0902733d5a8d47577303cb2842d268e7bc76d`**; checksum asset 26 agrees. Companion assets 21–24 also downloaded and verified.
|
||||
- Windows 11 build 26200, exact downloaded executables with bundled libunwind and system-only process PATH. Fresh extraction on a machine separate from the producer, unrelated desktop cwd, existing user profile. No local compilation or Linux checks; user explicitly excludes Linux for this prerelease.
|
||||
- This branch only adds QA documentation/evidence. The tested product stays at 715036e; later documentation commit identities are not rebuilt-product identities. Release/tag/publication ownership remains Codex-956atf.
|
||||
|
||||
## Results
|
||||
|
||||
| Scope | Result and limits |
|
||||
| --- | --- |
|
||||
| Download/package | All 565 archive files equal extraction; 564 checksum entries and 559 package input pins pass. Independent PE imports identify only bundled libunwind outside system components, no delay imports. 18 local doc links, 66 shipped source documents/examples, 489 notice references and 529 recorded local inputs checked. |
|
||||
| Build linkage | Both executable bytes equal the preserved **individual** final04 Cargo-identified builds for 715036e. All four BuildEvidence pins resolve to the published 206f3b9 gate evidence; all 462 archived evidence members verified. Supplemental combined-build hashes are distinct and excluded. No independent compiler rebuild claim. |
|
||||
| Native desktop | Bundled walkthrough loads from unrelated cwd; State add-bend visual Undo/Redo, bend movement with exact whole-source Undo/Redo, independent view navigation, dropdown-only Escape, explicit form Cancel, Save, normal exit and fresh-process reopen pass. Only one authored waypoint is added; semantics, attachments, node positions and all other views remain original bytes. Both runs exit 0 with empty logs. |
|
||||
| Built-in guide | Engineering guide and State topic render in the packaged app. A network-disabled environment was not tested. |
|
||||
| Server | 23 explicit runtime checks and 11 captured HTTP exchanges: version/help, readiness, served OpenAPI, complete authored CRLF/Unicode source, conditional name-only revision 1→2, stale ETag 412, exact model/source/store and acknowledged-write restart persistence. Both owned processes stopped. Forced exit 1 was intentional; no graceful shutdown, in-flight or power-loss claim. |
|
||||
|
||||
Detailed reports: [package/build review](package-provenance-review.md), [native observations](native-observations.md), [independent native/source review](native-source-review.md), [picker-policy review](picker-policy-review.md), [server review](server-review.md).
|
||||
|
||||
At 56% Fit the State fixture's labels remain small/crowded. These results do not establish broad editing usability or accessibility, aggregate deletion, modifier gestures, every new-feature combination, authentication/load/TLS, Linux, or complete product acceptance. The fixture's 12 model checks are intentional incompleteness. The second Escape **kept** the ordinary State edit form; explicit Cancel closed it. Capture 26's optimistic filename is contradicted by its retained screenshot and corrected in the reports. No in-memory history-stack introspection was performed.
|
||||
|
||||
## Portable evidence and reproduction
|
||||
|
||||
[evidence.zip](evidence.zip) contains **190 original/helper/review records**, **3,425,348 bytes**, SHA256 **`cb084be5f316f1b3be45bc5493fe0cbd6076052198c09f9dbac441b8f2588e63`**. [manifest.json](manifest.json) binds every member's size/hash and the ZIP; [checksum](evidence.zip.sha256) is supplied separately. This is evidence only: runnable product remains the release asset, not duplicated in Git.
|
||||
|
||||
Extract the ZIP into a new directory. Run `python -B native-check.py` and `python -B server-readback-check.py` there for offline full-source and captured-HTTP reconciliation. Retained successful results are `native-check.json` and `server-readback-check.json`. Original captures/metadata and launch/exit records live in `native-2/` and `native-3/`; baseline and all five saved source snapshots are included. `server-smoke/` contains the exact helper, requests/responses, source, accepted envelope, isolated store and logs.
|
||||
|
||||
Static package audits require the same release assets extracted at the original relative layout under `.local/package-qa/download-1`, a source checkout at 715036e, and `scripts/package-artifacts.py`. The archived audit helpers retain their original checkout-relative paths. `package-audit/build-link.py` also requires fetched commit `206f3b969482241e01e2dfc6be955caf31747b89`. It resolves `harness/prerelease37-windows-gates-20260924/final04-complete`, whose archive is SHA256 `7cead0b6aa36a6af8fc9f8987186bfe0decd8fa2ebab094fc45387ae9daea632`. The initial static result's producer-local `available_on_this_machine:false` fields are historical; subsequent `build-link-records.json` resolves them using the published raw evidence. Native/server launch helpers describe the performed environment; use a fresh evidence directory for a new runtime trial rather than overwriting these records.
|
||||
|
||||
The reports retain harness-only corrections: environment-key casing before first launch, native dialog automation limitations, and explicit UTF-8 decoding in parent HTTP reconciliation. None changed product bytes or converted a failed product trial into a pass.
|
||||
|
||||
[Final independent archive review](final-evidence-review.md) verified all 190 members against retained originals, bound the baseline to the companion ZIP, and replayed both offline evaluators from a fresh extraction with unchanged files and exact recorded results. That review is outside the already-frozen evidence ZIP.
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
cb084be5f316f1b3be45bc5493fe0cbd6076052198c09f9dbac441b8f2588e63 evidence.zip
|
||||
@@ -0,0 +1,28 @@
|
||||
# Independent final evidence audit
|
||||
|
||||
2026-09-24. Reviewed `harness/prerelease37-independent-9e8r6t-20260924` before publication. **No material finding within this bounded audit.** The package preserves the original evidence, both advertised portable read-only replays pass from a fresh extraction, and the README/report claims remain appropriately scoped.
|
||||
|
||||
## Archive and original fidelity
|
||||
|
||||
Independently rehashed `evidence.zip`: 3,425,348 bytes, SHA256 `cb084be5f316f1b3be45bc5493fe0cbd6076052198c09f9dbac441b8f2588e63`. This exactly matches the manifest, adjacent checksum and README. There are 190 unique members with no case-insensitive duplicate, traversal, absolute or backslash-ambiguous paths. Every member size/hash matches the manifest. Every member also equals its retained original under `.local/package-qa`; the separately named baseline and package metadata were mapped to the original companion extraction and Studio extraction. All five outer detailed reports equal their archived and original copies.
|
||||
|
||||
The native baseline is 9,662 bytes, SHA256 `2dae785d63a940094d8052f3dcddcb988c024b38df9780e026bccd516264dbfe`. It exactly equals the fixture member inside the archived State/Traceability companion ZIP, whose checksum also passes. Thus the portable native evaluator's baseline is independently tied to the downloaded companion, rather than merely accepted as a newly authored expected file. Both native start receipts' download-proof and launcher pins match archived bytes, and their executable/runtime pins equal the actual retained downloaded Studio executable and libunwind bytes.
|
||||
|
||||
The earlier package provenance audit remains applicable: downloaded Studio archive `5554e325...`, source `715036e132677dd2bc2fb37ed316d93dc9987aec`, and both executables' linkage to the preserved individual final04 Cargo-identified outputs. The final README correctly describes the separately verified 206f3b9 evidence and does not substitute the supplemental combined-build bytes. Historical producer-local unavailable-path fields in the first static result are explained by the later build-link records.
|
||||
|
||||
## Independent portable replay
|
||||
|
||||
Created a new isolated extraction at `.local/package-qa/final-review-replay-xkq9rytf`, outside both the immutable published candidate and the original run directories. Ran each extracted script using the bundled Python with `-B`, with the extraction as working directory:
|
||||
|
||||
- `native-check.py`: exit 0, empty stderr, output JSON exactly equal to archived `native-check.json`.
|
||||
- `server-readback-check.py`: exit 0, empty stderr, output JSON exactly equal to archived `server-readback-check.json`.
|
||||
|
||||
Rehashed every extracted file before and after each execution: no file changed or appeared. The scripts perform no product launch, network request or source build. The native replay compares all five full-source snapshots against baseline plus the single exact waypoint addition; movement Undo/Redo, explicit cancellation and fresh-process save retain the stated complete-file invariants. The HTTP replay checks all 11 captured response statuses, exact CRLF/Unicode source bundles, name-only model change, revisions/ETags, stale-write refusal, restart envelope and on-disk store equality. Its 23-check count describes the recorded runtime helper, not 23 newly executed runtime checks.
|
||||
|
||||
## Claims and selected visual review
|
||||
|
||||
Read the README and all five detailed reports, plus the two replay scripts and assembler. The top-level report clearly distinguishes visible add-bend Undo/Redo from saved full-source movement Undo/Redo, existing user profile from a clean OS/profile, rendered guide content from a network-disabled test, normal desktop exit from intentionally forced server termination, and scoped QA from full prerelease/product acceptance.
|
||||
|
||||
Manually inspected the original full-window `native-2/26-second-escape-cancels-form-1.jpg`: the ordinary State Edit transition form is visibly still present. The README and native/source/policy reports correctly override that optimistic filename with observed retention and identify explicit Cancel separately. Inspected `native-3/05-persisted-bend-selected-1.jpg`: the reopened edit-attachments view shows the selected Activate route and two square bend handles, including the downward bend. Its labels are small/crowded at 56%, consistently disclosed as a usability limitation. Exact coordinate and unrelated-source preservation claims come from the byte oracle, not pixel inference.
|
||||
|
||||
The server report preserves the two imposed exit-1 results and limits persistence to already acknowledged writes; it makes no graceful shutdown, in-flight write, power-loss, authentication, TLS, load or Linux claim. Helper-only corrections and the original capture labels remain disclosed. No product runtime/build, Git mutation, source edit or network action occurred in this audit. The only new artifacts are the fresh replay extraction and this review, outside the immutable evidence archive.
|
||||
@@ -0,0 +1,775 @@
|
||||
{
|
||||
"source": "715036e132677dd2bc2fb37ed316d93dc9987aec",
|
||||
"release": 7,
|
||||
"studioAsset": 25,
|
||||
"studioZip": {
|
||||
"bytes": 34168661,
|
||||
"sha256": "5554e32533e3cb2c33fb4143a5a0902733d5a8d47577303cb2842d268e7bc76d"
|
||||
},
|
||||
"members": {
|
||||
"assemble.py": {
|
||||
"bytes": 2742,
|
||||
"sha256": "a44cc7f41cc92ca42e070a4913be989bfd35fa1e27b1665db66bc2d5502fc02b"
|
||||
},
|
||||
"download-1/ArcadiaStudio-0.2.0-preview.integrated.20260924-x86_64-pc-windows-gnullvm.zip.sha256": {
|
||||
"bytes": 144,
|
||||
"sha256": "41c7b1257660b66a44f27555e0e9613e7f6e4c0b119522347e3ccff54d60cea1"
|
||||
},
|
||||
"download-1/ArcadiaStudio-Allocation-Process-checks-20260924.zip": {
|
||||
"bytes": 17190,
|
||||
"sha256": "17327e563af333fd9913f6d8b8a7e6ad0e095cea33393a5cad7a8b3397ffb185"
|
||||
},
|
||||
"download-1/ArcadiaStudio-Allocation-Process-checks-20260924.zip.sha256": {
|
||||
"bytes": 119,
|
||||
"sha256": "9e1052b1d6cc04f081f5657f69c4e7e22be9abae8d64ec1e9eb210e4d1f34bbb"
|
||||
},
|
||||
"download-1/ArcadiaStudio-State-Traceability-checks-20260924.zip": {
|
||||
"bytes": 9691,
|
||||
"sha256": "6fda3606b4fe811906a60fbb8a3ba594f826d285dc2b4fc136ddccb64660d994"
|
||||
},
|
||||
"download-1/ArcadiaStudio-State-Traceability-checks-20260924.zip.sha256": {
|
||||
"bytes": 119,
|
||||
"sha256": "8d6ab2c2a4ffeda42616a737c3fa514c2c94b33032dff6b12666d6313e77e324"
|
||||
},
|
||||
"download-1/assets.json": {
|
||||
"bytes": 2684,
|
||||
"sha256": "3312039a6d86262672e7d0a073faf5bd7dc88807db0a34a0400fef2773a6ba8f"
|
||||
},
|
||||
"download-1/download-result.json": {
|
||||
"bytes": 157695,
|
||||
"sha256": "198685f4d1e338857b9c2a61f7c046a66618ff8b88e2064435d3652ae6f4f73f"
|
||||
},
|
||||
"download-1/release.json": {
|
||||
"bytes": 5182,
|
||||
"sha256": "037816d561ff6d75e6d45dc98a578a47f70f105f30da2e91d1d0dfe317d77c1e"
|
||||
},
|
||||
"download.py": {
|
||||
"bytes": 3838,
|
||||
"sha256": "4353f4bff5d1ecba90e70725384c9d5e6b6d3f8ce970d1f22a0faac5a338f1a2"
|
||||
},
|
||||
"native-2/01-home-0.jpg": {
|
||||
"bytes": 103896,
|
||||
"sha256": "7db5300ebe5a1eb9e7135d33975531f643e205601f669e3bf25fb9745b3994ff"
|
||||
},
|
||||
"native-2/01-home.json": {
|
||||
"bytes": 981,
|
||||
"sha256": "7a40f7186e468099d7e7837c3968a08ad4e13f12d08f58071fd102fbeaaf7a83"
|
||||
},
|
||||
"native-2/02-maximized-home-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/02-maximized-home-1.jpg": {
|
||||
"bytes": 105249,
|
||||
"sha256": "48ced23c34ac07509446511e700892478e3f9876004f7a5b5bd120cd65b75e22"
|
||||
},
|
||||
"native-2/02-maximized-home.json": {
|
||||
"bytes": 1110,
|
||||
"sha256": "d3487f8a4ee43932afed3c6cb6843334bff1f137e7d16463b3e14d9e458da1d1"
|
||||
},
|
||||
"native-2/03-bundled-walkthrough-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/03-bundled-walkthrough-1.jpg": {
|
||||
"bytes": 103761,
|
||||
"sha256": "e334cc9addc67390881d9b4629646643f6eb57b4aed8b488deea25503c00b7bd"
|
||||
},
|
||||
"native-2/03-bundled-walkthrough.json": {
|
||||
"bytes": 1120,
|
||||
"sha256": "161db29fd86461c77a4411ec7ea4e74d822ab54dcbfb86ceeeb6bab8337d00b9"
|
||||
},
|
||||
"native-2/04-walkthrough-loaded-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/04-walkthrough-loaded-1.jpg": {
|
||||
"bytes": 142209,
|
||||
"sha256": "d566beb4d45998de78ea57a01c39a92ac3cb3db5d268016247b9427694f3c8f9"
|
||||
},
|
||||
"native-2/04-walkthrough-loaded.json": {
|
||||
"bytes": 1303,
|
||||
"sha256": "d913fbee41d5e069a1f1eee227d1470ce752880b38c1f283d01bc931bf0c347f"
|
||||
},
|
||||
"native-2/05-state-fixture-open-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/05-state-fixture-open-1.jpg": {
|
||||
"bytes": 110683,
|
||||
"sha256": "b8fadad5f4f8cee676ef288131e336236531a7c7c9dc383c22b479fd19823d7c"
|
||||
},
|
||||
"native-2/05-state-fixture-open.json": {
|
||||
"bytes": 1303,
|
||||
"sha256": "cd991ce13b60ef70234950bb96efdecccb6c719ffb0b10cd377858e873f9b70a"
|
||||
},
|
||||
"native-2/06-system-perspective-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/06-system-perspective-1.jpg": {
|
||||
"bytes": 129420,
|
||||
"sha256": "4841b3d79220bfbc955a619ce0ddd81b4c75acc471c5f9f15860adb8d6f4bcba"
|
||||
},
|
||||
"native-2/06-system-perspective.json": {
|
||||
"bytes": 1243,
|
||||
"sha256": "4b2dd65851b41c33c4cdd26a7e0b98d16171d60aba52fe9851b9582ea1ac572a"
|
||||
},
|
||||
"native-2/07-activate-transition-selected-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/07-activate-transition-selected-1.jpg": {
|
||||
"bytes": 129572,
|
||||
"sha256": "3c176faf194ed25517fab1762ba2419c61777d8df78d27b43c85de252062fc8a"
|
||||
},
|
||||
"native-2/07-activate-transition-selected.json": {
|
||||
"bytes": 1263,
|
||||
"sha256": "3ecc247a8c0bae174df3ba4887213f0b77e4b0c48232a1aab2720ced81e68161"
|
||||
},
|
||||
"native-2/08-transition-actions-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/08-transition-actions-1.jpg": {
|
||||
"bytes": 132017,
|
||||
"sha256": "d33691c256cfe24eb910be9cda23cc851dae3ea42537a3b389dead0a9503a16c"
|
||||
},
|
||||
"native-2/08-transition-actions.json": {
|
||||
"bytes": 1243,
|
||||
"sha256": "9d4f6ebe683701a28ff4c2bb2941da680c3adbbd4526374ab35654d51ee2b23c"
|
||||
},
|
||||
"native-2/09-add-bend-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/09-add-bend-1.jpg": {
|
||||
"bytes": 128200,
|
||||
"sha256": "22a5382b80b22e5e7e8b539d5e1d3c1718a83fbe6d7c304c0efd9c068b7f22e8"
|
||||
},
|
||||
"native-2/09-add-bend.json": {
|
||||
"bytes": 1231,
|
||||
"sha256": "ccaebd7e516f9f02efa686b5ee3fcc3a00a64079e0bf3347a94e8990c7b790ba"
|
||||
},
|
||||
"native-2/10-undo-added-bend-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/10-undo-added-bend-1.jpg": {
|
||||
"bytes": 126682,
|
||||
"sha256": "f51fe83cb7796bb43bacd66119cc77f74f7508b71638c10b8594503517268044"
|
||||
},
|
||||
"native-2/10-undo-added-bend.json": {
|
||||
"bytes": 1239,
|
||||
"sha256": "f2a4233e6fdbeb35b44d79a76f3a0aacbe4775ac3254b792314ea0a2fb52a259"
|
||||
},
|
||||
"native-2/11-redo-added-bend-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/11-redo-added-bend-1.jpg": {
|
||||
"bytes": 126800,
|
||||
"sha256": "561f1e4524b7d6c4d33b00e3047fc3b48a79d3801933431f01df800ba0b92460"
|
||||
},
|
||||
"native-2/11-redo-added-bend.json": {
|
||||
"bytes": 1245,
|
||||
"sha256": "44c5184cd68b2ee319e913c02e67528aec78432317bddde5622b3a3d00508032"
|
||||
},
|
||||
"native-2/12-move-new-bend-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/12-move-new-bend-1.jpg": {
|
||||
"bytes": 128606,
|
||||
"sha256": "d99f2ff1e75f69181cc700f8c6ee71790b300f4e4eae37e48a70548335795aca"
|
||||
},
|
||||
"native-2/12-move-new-bend.json": {
|
||||
"bytes": 1243,
|
||||
"sha256": "544c719817b52d84e432161f90acc23248f753cec53dfc7093a4a460f6cd3604"
|
||||
},
|
||||
"native-2/13-save-bend-edit-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/13-save-bend-edit-1.jpg": {
|
||||
"bytes": 126952,
|
||||
"sha256": "1038eaf6e03d1b1652ad0d7a87fa3e4fde619e86dd2c31a1f81002783e482e9f"
|
||||
},
|
||||
"native-2/13-save-bend-edit.json": {
|
||||
"bytes": 1237,
|
||||
"sha256": "2fffb1f9fc0575db2157bd4b4facff13833a812237e826700269863d4c22dd54"
|
||||
},
|
||||
"native-2/14-undo-bend-move-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/14-undo-bend-move-1.jpg": {
|
||||
"bytes": 126804,
|
||||
"sha256": "15408b738a4508b64ef47151ff3b762ee1316db03837b2fba5ebd6044b290791"
|
||||
},
|
||||
"native-2/14-undo-bend-move.json": {
|
||||
"bytes": 1243,
|
||||
"sha256": "5d98c93c27ea062d4cebc15c26defcd3e6a5b21c793b5f6b1cea35a565f59938"
|
||||
},
|
||||
"native-2/15-save-undone-move-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/15-save-undone-move-1.jpg": {
|
||||
"bytes": 126302,
|
||||
"sha256": "26bd1d6d2dfb01667d756d93ba4e94f3023af65a8574707b8e9d894225bac546"
|
||||
},
|
||||
"native-2/15-save-undone-move.json": {
|
||||
"bytes": 1241,
|
||||
"sha256": "768cae7204c2bd0fb833b68eebdec718311b736803b0418477e26f6e21a4a1b9"
|
||||
},
|
||||
"native-2/16-redo-bend-move-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/16-redo-bend-move-1.jpg": {
|
||||
"bytes": 127338,
|
||||
"sha256": "4188744410f21061b4a3f09dd2e69566256a2a8f0cd82f23eea88f7bbcb12c9f"
|
||||
},
|
||||
"native-2/16-redo-bend-move.json": {
|
||||
"bytes": 1243,
|
||||
"sha256": "7bb46dc2c1318ff8ce0a134692d4567a0fba1cf468c44ca2389c6e62f8b2b5e2"
|
||||
},
|
||||
"native-2/17-save-redone-move-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/17-save-redone-move-1.jpg": {
|
||||
"bytes": 126803,
|
||||
"sha256": "7f29d34336c5fd7bc45677e542be6c7cbb9d5aa1255a5adb8e4f76453ffb8df2"
|
||||
},
|
||||
"native-2/17-save-redone-move.json": {
|
||||
"bytes": 1241,
|
||||
"sha256": "c4a1fb2ee1dd6c26be9bbfe2559813133cf7174d17622d5a8446160e3bd5b224"
|
||||
},
|
||||
"native-2/18-state-view-picker-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/18-state-view-picker-1.jpg": {
|
||||
"bytes": 129077,
|
||||
"sha256": "99f8ffd05b6cbc400712180d4749410aae4276dc288cc69d535021cc4d61b1a4"
|
||||
},
|
||||
"native-2/18-state-view-picker.json": {
|
||||
"bytes": 1241,
|
||||
"sha256": "81d52aca8a4870d10be4ee48459ebc940b5c9399817f74c03f7bb2c545b6370f"
|
||||
},
|
||||
"native-2/19-reference-view-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/19-reference-view-1.jpg": {
|
||||
"bytes": 126293,
|
||||
"sha256": "a3653870e1afcc8808b4627bc435938f97eb9487e00b91b2278fbc7265bf9cda"
|
||||
},
|
||||
"native-2/19-reference-view.json": {
|
||||
"bytes": 1235,
|
||||
"sha256": "625a6587819f203fa8b82ca86a32cbf6b1853de6d7ee8024177adbed8f7eee74"
|
||||
},
|
||||
"native-2/20-reference-picker-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/20-reference-picker-1.jpg": {
|
||||
"bytes": 129268,
|
||||
"sha256": "de07f6abf1df6ccfa2d1461f0e9d03dc5a8c59d1b999e99b47ad548ad0e33f3a"
|
||||
},
|
||||
"native-2/20-reference-picker.json": {
|
||||
"bytes": 1239,
|
||||
"sha256": "37d6b22eda964fc5d270c3699b42de77012ed91ae02b809320517eaa6414043f"
|
||||
},
|
||||
"native-2/21-edited-view-return-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/21-edited-view-return-1.jpg": {
|
||||
"bytes": 126971,
|
||||
"sha256": "c9ae903418fa95118ed00ebc17026e52224cefdb075740f38784f90d97a375be"
|
||||
},
|
||||
"native-2/21-edited-view-return.json": {
|
||||
"bytes": 1243,
|
||||
"sha256": "95bdfe3e5150e353be6bbc54722bf5d10b5ef526da2589d3c65771a10851119b"
|
||||
},
|
||||
"native-2/22-transition-actions-reopen-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/22-transition-actions-reopen-1.jpg": {
|
||||
"bytes": 130710,
|
||||
"sha256": "850d1ffe7e4ec1eafdcd9056c115f1c50d4f310112b2502c23c3e7ee7667c2fc"
|
||||
},
|
||||
"native-2/22-transition-actions-reopen.json": {
|
||||
"bytes": 1257,
|
||||
"sha256": "4c256bdee194721424a98bca0cc4ac5df259d45174ccfd6dc1a9275b4a959644"
|
||||
},
|
||||
"native-2/23-transition-edit-form-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/23-transition-edit-form-1.jpg": {
|
||||
"bytes": 134465,
|
||||
"sha256": "d6b2a6d00544c84eaaa66481621eb70281b22eabe86539d54489cd28b997f54e"
|
||||
},
|
||||
"native-2/23-transition-edit-form.json": {
|
||||
"bytes": 1255,
|
||||
"sha256": "0a8f47170b1e9c6ab183401ac94a6a7af56b98d6f18248e2cc88aa4dbf0ce09f"
|
||||
},
|
||||
"native-2/24-transition-source-picker-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/24-transition-source-picker-1.jpg": {
|
||||
"bytes": 132783,
|
||||
"sha256": "fcb23f584ecb4470122c4a2276dfe92d3e8b20754e0a641c6cf53c50a2cbe883"
|
||||
},
|
||||
"native-2/24-transition-source-picker.json": {
|
||||
"bytes": 1265,
|
||||
"sha256": "a2af6cde560ffe2ce114c1ba5019ebd81af45960249ef1af040b0d63f85fa996"
|
||||
},
|
||||
"native-2/25-first-escape-picker-only-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/25-first-escape-picker-only-1.jpg": {
|
||||
"bytes": 134562,
|
||||
"sha256": "a45837ea333a05b50be5c081378cdce51b9c302ca49ce956389063e83906b63d"
|
||||
},
|
||||
"native-2/25-first-escape-picker-only.json": {
|
||||
"bytes": 1265,
|
||||
"sha256": "2d153f32fcfa82171a4f9721b1148bdcca32560f93ab75d8bb9a46dfdf87d6fb"
|
||||
},
|
||||
"native-2/26-second-escape-cancels-form-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/26-second-escape-cancels-form-1.jpg": {
|
||||
"bytes": 134562,
|
||||
"sha256": "a45837ea333a05b50be5c081378cdce51b9c302ca49ce956389063e83906b63d"
|
||||
},
|
||||
"native-2/26-second-escape-cancels-form.json": {
|
||||
"bytes": 1269,
|
||||
"sha256": "38d9b72fd6a36a524ba281c798f4b3266d13c82761da3c62d32f804b1131a062"
|
||||
},
|
||||
"native-2/27-second-escape-settled-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/27-second-escape-settled-1.jpg": {
|
||||
"bytes": 134562,
|
||||
"sha256": "a45837ea333a05b50be5c081378cdce51b9c302ca49ce956389063e83906b63d"
|
||||
},
|
||||
"native-2/27-second-escape-settled.json": {
|
||||
"bytes": 1259,
|
||||
"sha256": "8c3ac677ba291dd3a3c59345e78d36844fb93e5be69c960f7e25c5fb23a19a9e"
|
||||
},
|
||||
"native-2/28-explicit-cancel-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/28-explicit-cancel-1.jpg": {
|
||||
"bytes": 127338,
|
||||
"sha256": "af811b0432a4dbf16c0312af2c4222ede3abccbed189d1ce238382abef0c4146"
|
||||
},
|
||||
"native-2/28-explicit-cancel.json": {
|
||||
"bytes": 1239,
|
||||
"sha256": "d1b45b5bd046acdff26737d9c3461f03383d2d5ec3f017afd4c1d4dcd01a9721"
|
||||
},
|
||||
"native-2/29-save-after-form-cancel-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-2/29-save-after-form-cancel-1.jpg": {
|
||||
"bytes": 127312,
|
||||
"sha256": "81228aae86327fbc8a937c93c6919424d9fdd9273f65d773964488fe7813a655"
|
||||
},
|
||||
"native-2/29-save-after-form-cancel.json": {
|
||||
"bytes": 1251,
|
||||
"sha256": "e8ac53acde40e4634e101bf199795bdec32448a94d065b55eb251e28bb53ccc2"
|
||||
},
|
||||
"native-2/after-cancel-saved.arcadia": {
|
||||
"bytes": 9705,
|
||||
"sha256": "ea05d0e409687c7943dda0b93bc34036eb524f5f5033b912e71ad2eaf643a158"
|
||||
},
|
||||
"native-2/bend-saved.arcadia": {
|
||||
"bytes": 9705,
|
||||
"sha256": "ea05d0e409687c7943dda0b93bc34036eb524f5f5033b912e71ad2eaf643a158"
|
||||
},
|
||||
"native-2/finished.json": {
|
||||
"bytes": 354,
|
||||
"sha256": "4094c500ce0668ef6f0cba4e8ac43e3630d483be3a1b4f94584ce004e7826f9e"
|
||||
},
|
||||
"native-2/move-redo-saved.arcadia": {
|
||||
"bytes": 9705,
|
||||
"sha256": "ea05d0e409687c7943dda0b93bc34036eb524f5f5033b912e71ad2eaf643a158"
|
||||
},
|
||||
"native-2/move-undo-saved.arcadia": {
|
||||
"bytes": 9705,
|
||||
"sha256": "7f1fc18265a79b7d452e4a3de3f17671684e38c0f727cbdec15833196f46f114"
|
||||
},
|
||||
"native-2/started.json": {
|
||||
"bytes": 891,
|
||||
"sha256": "cd12917f9016f3f6e271ddf0a8eec1bc2cfd4fc2c65389380f9ecb43aaca1643"
|
||||
},
|
||||
"native-2/stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"native-2/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"native-3/01-fresh-home-0.jpg": {
|
||||
"bytes": 101565,
|
||||
"sha256": "1823169b7164d38e81c38b83f2c3927f9f4b37f6fb59732d3387fa1791dbaff4"
|
||||
},
|
||||
"native-3/01-fresh-home.json": {
|
||||
"bytes": 988,
|
||||
"sha256": "a1562b3b2d66aa3d25b88cd693a505f9db7cac07177088d0d529ba19ea4cc1a9"
|
||||
},
|
||||
"native-3/02-maximized-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-3/02-maximized-1.jpg": {
|
||||
"bytes": 105356,
|
||||
"sha256": "f12eb6b9e8ce8606ffffd89fdd916cd6562492de3e69a7f104163816580f4107"
|
||||
},
|
||||
"native-3/02-maximized.json": {
|
||||
"bytes": 1101,
|
||||
"sha256": "ec1750b82cd0ffb5964e265ec2d5a027225e374e3afd6d9616eff38f5b5ed398"
|
||||
},
|
||||
"native-3/03-open-saved-fixture-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-3/03-open-saved-fixture-1.jpg": {
|
||||
"bytes": 110813,
|
||||
"sha256": "7a187538b69c82907fe301d778acae5fdbed094d87d9646942f0c324ae44df79"
|
||||
},
|
||||
"native-3/03-open-saved-fixture.json": {
|
||||
"bytes": 1119,
|
||||
"sha256": "c22071d278a4c2f4b9fb614bee7f3f0a707a8e82ecf7f0a78765bf16dc6f562b"
|
||||
},
|
||||
"native-3/04-system-state-reopened-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-3/04-system-state-reopened-1.jpg": {
|
||||
"bytes": 129878,
|
||||
"sha256": "2db55e8cc7f603c08b151cc020214ddd7a103d0bdb59e6d04bf5cd6a42fc3119"
|
||||
},
|
||||
"native-3/04-system-state-reopened.json": {
|
||||
"bytes": 1250,
|
||||
"sha256": "356f04db8dfa782a5816d050fd8a7c9a09a9cb5f58f5619ee90eca96b759314c"
|
||||
},
|
||||
"native-3/05-persisted-bend-selected-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-3/05-persisted-bend-selected-1.jpg": {
|
||||
"bytes": 129942,
|
||||
"sha256": "118a7bfb22bff5099a6f7008e234f85102992834d80d66510ee898562a3844b7"
|
||||
},
|
||||
"native-3/05-persisted-bend-selected.json": {
|
||||
"bytes": 1254,
|
||||
"sha256": "0279ec95b15e265c770c5168c34412ead55a18fbd38b2f47169f5b325c51f28a"
|
||||
},
|
||||
"native-3/06-save-after-reopen-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-3/06-save-after-reopen-1.jpg": {
|
||||
"bytes": 128388,
|
||||
"sha256": "e9185c8e612020d05cce72f995d21f22e6e832f05a63c66bf4c56f02639a9905"
|
||||
},
|
||||
"native-3/06-save-after-reopen.json": {
|
||||
"bytes": 1242,
|
||||
"sha256": "d74f726ff589336db1772cc8d7988d8c73f3c6d63973e6871324a3a3232cd632"
|
||||
},
|
||||
"native-3/07-engineering-guide-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-3/07-engineering-guide-1.jpg": {
|
||||
"bytes": 173028,
|
||||
"sha256": "1e9a9cdc315c7e12901ba3a5bd1dec6cfc2f5c3628d40346ade986c19b4e4254"
|
||||
},
|
||||
"native-3/07-engineering-guide.json": {
|
||||
"bytes": 1242,
|
||||
"sha256": "53a5822d73022eaed8c73b1d763d2a89facbc1878bc06ab6eecb18890af2ca9a"
|
||||
},
|
||||
"native-3/08-state-guide-topic-0.jpg": {
|
||||
"bytes": 722,
|
||||
"sha256": "d89da17fb8517188426907d542e2b5f91098a2e8d121506b6e65506e40de865d"
|
||||
},
|
||||
"native-3/08-state-guide-topic-1.jpg": {
|
||||
"bytes": 198585,
|
||||
"sha256": "0bcab4102666a794fd791561a4ef4ff44add5bfbb377cb9e79fb0cae3e57cbc9"
|
||||
},
|
||||
"native-3/08-state-guide-topic.json": {
|
||||
"bytes": 1242,
|
||||
"sha256": "2d7ba83836ec93ad6ee7104a26754cd42edffda055c5fa7305926557827b3807"
|
||||
},
|
||||
"native-3/finished.json": {
|
||||
"bytes": 354,
|
||||
"sha256": "e6853bb83c949db2828bedc8d7763012673ccb378f8172cbee6dfeb7efa3043d"
|
||||
},
|
||||
"native-3/reopened-saved.arcadia": {
|
||||
"bytes": 9705,
|
||||
"sha256": "ea05d0e409687c7943dda0b93bc34036eb524f5f5033b912e71ad2eaf643a158"
|
||||
},
|
||||
"native-3/started.json": {
|
||||
"bytes": 891,
|
||||
"sha256": "776ea6df056bf0204c1db8849e59e46fde961abee98823e8fdb86e95faa197ea"
|
||||
},
|
||||
"native-3/stderr.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"native-3/stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"native-baseline.arcadia": {
|
||||
"bytes": 9662,
|
||||
"sha256": "2dae785d63a940094d8052f3dcddcb988c024b38df9780e026bccd516264dbfe"
|
||||
},
|
||||
"native-check.json": {
|
||||
"bytes": 1247,
|
||||
"sha256": "70f2fce9d03809ca178c2e94f4bd9802168c6c93e920f091f5960d7c165a4e1d"
|
||||
},
|
||||
"native-check.py": {
|
||||
"bytes": 2210,
|
||||
"sha256": "5c1f6aaa22c2c1d5abc0339ab0186f65e0ef3e58555681cb7a0a493024a875a7"
|
||||
},
|
||||
"native-launch.py": {
|
||||
"bytes": 2545,
|
||||
"sha256": "c6338d2234db3453bccaa65c9e2df3b48b3eadc206605762afc11fd2ad11b895"
|
||||
},
|
||||
"native-observations.md": {
|
||||
"bytes": 5929,
|
||||
"sha256": "878369a46ed0ef58d92b00ca008bcb1729fee216a5a7aeb7b637130872ed5b42"
|
||||
},
|
||||
"native-source-review.md": {
|
||||
"bytes": 6540,
|
||||
"sha256": "4216547e3aa8902f7c9b6f66e5d2ef8f10f50d3b815b2abf9398edd8b942ab0c"
|
||||
},
|
||||
"package-audit/audit.py": {
|
||||
"bytes": 7776,
|
||||
"sha256": "bb2a3877d25952814654652f1f5b25f3e5e2ae95063c0c244b80e07c06a22580"
|
||||
},
|
||||
"package-audit/build-link-records.json": {
|
||||
"bytes": 30112,
|
||||
"sha256": "9e52981badd31675982c6d358cb193d1d074cff777a2974eef3109b8a90f78d7"
|
||||
},
|
||||
"package-audit/build-link.py": {
|
||||
"bytes": 3781,
|
||||
"sha256": "e56e98a1502033fb9349e8313e075083c78ff0ca0c65eea0f619ca21d701a2b3"
|
||||
},
|
||||
"package-audit/result.json": {
|
||||
"bytes": 3458,
|
||||
"sha256": "937eefdffdf70159d4aa58b7042c47e10bf823d96bdf082acecb7ff92e2937d0"
|
||||
},
|
||||
"package-metadata/README.md": {
|
||||
"bytes": 1902,
|
||||
"sha256": "597cfb8da873e019d2658420f18ec059f0185fa9106ff9eafe934dfdd6ed266b"
|
||||
},
|
||||
"package-metadata/SHA256SUMS.txt": {
|
||||
"bytes": 67036,
|
||||
"sha256": "5fb408d7279f733daddf84ae59e4187c935d40738e420ad5bb7fa01641cbc693"
|
||||
},
|
||||
"package-metadata/START-HERE.txt": {
|
||||
"bytes": 676,
|
||||
"sha256": "a549295cf57ac73673dc4fa6106463450f982122793cb9c7135ce4fc7087e9ed"
|
||||
},
|
||||
"package-metadata/documentation-check.json": {
|
||||
"bytes": 43,
|
||||
"sha256": "18a35f7022d6c180c42ad4fd7c7010e494bdff0d2ca395efac84ac545fe23627"
|
||||
},
|
||||
"package-metadata/package.json": {
|
||||
"bytes": 76306,
|
||||
"sha256": "daf412ef5bd6facfe471181cc4c57203c25bc2b952e533d0e170a06302d0ea32"
|
||||
},
|
||||
"package-provenance-review.md": {
|
||||
"bytes": 6178,
|
||||
"sha256": "9ce825b952172b8e6e3ab8652ded4e6d5ab8666fbd55ebc9a7d4dc5d3b02ec2d"
|
||||
},
|
||||
"picker-policy-review.md": {
|
||||
"bytes": 5213,
|
||||
"sha256": "58d0bfb810762b46660748436e7ca69235a834cae62bd1a960b6158881b4154f"
|
||||
},
|
||||
"server-readback-check.json": {
|
||||
"bytes": 372,
|
||||
"sha256": "160a5978a49d1765febe6fcca31c0dbf604d6275a4f4c78160c04f83b6243859"
|
||||
},
|
||||
"server-readback-check.py": {
|
||||
"bytes": 2850,
|
||||
"sha256": "4e20463f5b62f83e0908a7deb1204d37b2e89979be6c1dc06b4ea7daa7569820"
|
||||
},
|
||||
"server-review.md": {
|
||||
"bytes": 4800,
|
||||
"sha256": "bd02d4b12a29a6b71ec91f381073dde736e887793c57ea5127e5313088ede167"
|
||||
},
|
||||
"server-smoke/01.request.http": {
|
||||
"bytes": 72,
|
||||
"sha256": "98a7941518fa378830e47e212bcab4500ab3ae951d13b79911acc92e61742b08"
|
||||
},
|
||||
"server-smoke/01.response.http": {
|
||||
"bytes": 219,
|
||||
"sha256": "0cc33f39df17d6b4a5ca28402b8719455696a694e385e1898cc7ce403d2f364f"
|
||||
},
|
||||
"server-smoke/02.request.http": {
|
||||
"bytes": 79,
|
||||
"sha256": "52cab473c448bd13e88e2c866e05071c5ebba40058db73ced7fb37779683e4e7"
|
||||
},
|
||||
"server-smoke/02.response.http": {
|
||||
"bytes": 349010,
|
||||
"sha256": "faa794207009a9a2c7988e2b49f1ab07c0eac68c09de9baa96f2054aed8a8c16"
|
||||
},
|
||||
"server-smoke/03.request.http": {
|
||||
"bytes": 474,
|
||||
"sha256": "a24ed595021750bc44ece5c2d1366b56bfa86cf011c112ab2871107c3da0cc11"
|
||||
},
|
||||
"server-smoke/03.response.http": {
|
||||
"bytes": 535,
|
||||
"sha256": "16c3b7e6acca0d71cb2b20641fa8038c51329201bb4c9db78b35d8e3be26d854"
|
||||
},
|
||||
"server-smoke/04.request.http": {
|
||||
"bytes": 112,
|
||||
"sha256": "41c7650b095643b62e8e6024047dac7a24221859c4697257c0480864b186ed41"
|
||||
},
|
||||
"server-smoke/04.response.http": {
|
||||
"bytes": 1070,
|
||||
"sha256": "f8d46feb5f1fed3d336cfefb2c47b1cbad3108c80154a2f17e251d4ea081d88c"
|
||||
},
|
||||
"server-smoke/05.request.http": {
|
||||
"bytes": 127,
|
||||
"sha256": "0350f3adcfc326d86dbc6c73a9691027093e788ebc73478175ea824fbb347a6c"
|
||||
},
|
||||
"server-smoke/05.response.http": {
|
||||
"bytes": 579,
|
||||
"sha256": "24a3205e9ecfeef972e71dae666634d65d3892c644e63556171bc014e49f2b2c"
|
||||
},
|
||||
"server-smoke/06.request.http": {
|
||||
"bytes": 556,
|
||||
"sha256": "fc44c92bb375c80bd87fc2d8a5e2897481dba4fa05a2f9524a9b9277c1d01b7f"
|
||||
},
|
||||
"server-smoke/06.response.http": {
|
||||
"bytes": 1219,
|
||||
"sha256": "579d76a9f6bd3023aa3e1bb5c3b01baad83a40f2a2d7be6c177368fe4e4d436d"
|
||||
},
|
||||
"server-smoke/07.request.http": {
|
||||
"bytes": 561,
|
||||
"sha256": "33f43063a9a52dbe65f9b0a571a048bb351463098572a76339e9eb893fd2c10c"
|
||||
},
|
||||
"server-smoke/07.response.http": {
|
||||
"bytes": 450,
|
||||
"sha256": "9ec589caf67f59b80488ff308572ea67a7acc60d819b8342cc4506df50be97cf"
|
||||
},
|
||||
"server-smoke/08.request.http": {
|
||||
"bytes": 112,
|
||||
"sha256": "41c7650b095643b62e8e6024047dac7a24221859c4697257c0480864b186ed41"
|
||||
},
|
||||
"server-smoke/08.response.http": {
|
||||
"bytes": 1066,
|
||||
"sha256": "46e90ec83c926b2c702f8901a53da4e4a1a1943d25cc778c8e794b6876044cd9"
|
||||
},
|
||||
"server-smoke/09.request.http": {
|
||||
"bytes": 72,
|
||||
"sha256": "98a7941518fa378830e47e212bcab4500ab3ae951d13b79911acc92e61742b08"
|
||||
},
|
||||
"server-smoke/09.response.http": {
|
||||
"bytes": 219,
|
||||
"sha256": "80c9c431a2fdd254ae292358cbf8fb15ab50a09357c3a8c05bd9f2a155e12136"
|
||||
},
|
||||
"server-smoke/10.request.http": {
|
||||
"bytes": 112,
|
||||
"sha256": "41c7650b095643b62e8e6024047dac7a24221859c4697257c0480864b186ed41"
|
||||
},
|
||||
"server-smoke/10.response.http": {
|
||||
"bytes": 1066,
|
||||
"sha256": "12acd31db3adb0ef07bea483502f1b499e0bfbeda3a980366bcbed21a70ca83d"
|
||||
},
|
||||
"server-smoke/11.request.http": {
|
||||
"bytes": 127,
|
||||
"sha256": "0350f3adcfc326d86dbc6c73a9691027093e788ebc73478175ea824fbb347a6c"
|
||||
},
|
||||
"server-smoke/11.response.http": {
|
||||
"bytes": 577,
|
||||
"sha256": "07bc56fb0a7b1532f720c64e1444f95eb085578894d42c17fdcb56b73fa5f495"
|
||||
},
|
||||
"server-smoke/accepted-envelope.json": {
|
||||
"bytes": 1051,
|
||||
"sha256": "933a78f4cc01424942e37f146c679ff9a59ed470643ebae94553f9b51386dace"
|
||||
},
|
||||
"server-smoke/edited.arcadia": {
|
||||
"bytes": 249,
|
||||
"sha256": "e59acb5ed01e55057096b22a39667b40ddddb97227395d04c281686a73c6a14c"
|
||||
},
|
||||
"server-smoke/first.stderr.log": {
|
||||
"bytes": 120,
|
||||
"sha256": "3bcc24b254215618fd5aea59360a1a7b8f3c7eba3d98063f8ebd8b4234e971f6"
|
||||
},
|
||||
"server-smoke/first.stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"server-smoke/help.stderr.txt": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"server-smoke/help.stdout.txt": {
|
||||
"bytes": 350,
|
||||
"sha256": "99c5cb07d451c6da829b835b66fce92ce5c2529508c8053030123df704d0610e"
|
||||
},
|
||||
"server-smoke/original.arcadia": {
|
||||
"bytes": 251,
|
||||
"sha256": "1f6b88cbcc3ac12995192feca91e07b9f6d536f2d21d0df6f42aa08f5c745ba9"
|
||||
},
|
||||
"server-smoke/package-provenance.json": {
|
||||
"bytes": 76305,
|
||||
"sha256": "bcd528f4bc722a89bdf565d46c1e2ac474ab16b7a17512365c194c8da1319d49"
|
||||
},
|
||||
"server-smoke/restart.stderr.log": {
|
||||
"bytes": 120,
|
||||
"sha256": "3bcc24b254215618fd5aea59360a1a7b8f3c7eba3d98063f8ebd8b4234e971f6"
|
||||
},
|
||||
"server-smoke/restart.stdout.log": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"server-smoke/result.json": {
|
||||
"bytes": 4731,
|
||||
"sha256": "1e564b40fd65a7de3d9c251f239f67586e8a2060f541b7fa06451f5e7cb375f7"
|
||||
},
|
||||
"server-smoke/run.py": {
|
||||
"bytes": 9122,
|
||||
"sha256": "62f173e6dd284589639ca85029b3d343e73f6f4548193a203db66769ae12e018"
|
||||
},
|
||||
"server-smoke/store/.arcadia.lock": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"server-smoke/store/230d7234-8150-4252-a4bd-85a3b7b2308c.json": {
|
||||
"bytes": 810,
|
||||
"sha256": "c928995daf0c1ff3963850aba9ad533196423fa2112ad99554758825f73fc5af"
|
||||
},
|
||||
"server-smoke/version.stderr.txt": {
|
||||
"bytes": 0,
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
"server-smoke/version.stdout.txt": {
|
||||
"bytes": 21,
|
||||
"sha256": "af82a1126f98794babaa173139764cadbfb5143a235f9a887ef70d18d00a0d91"
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"bytes": 3425348,
|
||||
"sha256": "cb084be5f316f1b3be45bc5493fe0cbd6076052198c09f9dbac441b8f2588e63"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Downloaded Windows Studio: direct native observations
|
||||
|
||||
Codex-9e8r6t /root, 2026-09-24. Tested the unmodified fresh extraction of release 7 asset 25 on Windows 11 build 26200. Product source is `715036e132677dd2bc2fb37ed316d93dc9987aec`; no Cargo/build or Linux operation occurred in this exercise. This is independent of the producer's machine and GUI exercise.
|
||||
|
||||
## Launch and workflows
|
||||
|
||||
`native-launch.py` launched the packaged executable directly with PATH `C:\Windows\System32;C:\Windows`, removed development-related environment variables, and used unrelated working directory `C:/arcadia-package-qa-9e8r6t`. It retained the ordinary Windows user profile, including existing recovery entries; this is not a fresh-OS/profile test. Python only orchestrated the launch and logging. The original extracted bundle was not edited. A whole companion kit and example directory were copied to the working directory before opening the copied State model. Both application sessions closed normally with Alt+F4, exit 0, empty stdout/stderr and unchanged executable/runtime hashes.
|
||||
|
||||
Original screenshots and their timestamps/window identity/accessibility trees are in `native-2/` and `native-3/`. Each capture includes a full-window JPEG, plus a tiny icon image in most captures. Native actions used Computer Use, without application-state injection. Visual acceptance below is bounded to the actual observed sequence.
|
||||
|
||||
| Captures | Observed behavior |
|
||||
| --- | --- |
|
||||
| native-2 01–04 | Home launched; bundled water-pressure walkthrough loaded from the unrelated cwd (94 objects, 62 links, 1 model check). |
|
||||
| native-2 05–09 | Opened copied State fixture; selected System perspective, readiness view 03 and Activate transition; Actions → Add bend displayed an additional square handle. |
|
||||
| native-2 10–11 | Undo removed the added handle; Redo restored it. These two observations are visual, not saved full-file snapshots. |
|
||||
| native-2 12–17 | Dragged new square downward; Save, Undo movement/Save, Redo movement/Save. Whole-source checks establish one additional waypoint, original (500,310) retained, added point moving (526,310) → (530,385), and exact movement Undo/Redo. |
|
||||
| native-2 18–21 | Switched to untouched reference view 04, then returned to edited view 03. Reference layout remained distinct; the authored edit returned. Whole-file comparison also leaves every reference-view byte unchanged. |
|
||||
| native-2 22–29 | Opened Edit transition and its source-state dropdown without changing fields. First Escape closed the dropdown and kept the form. Second Escape kept the form too. Explicit Cancel closed it; saving preserved the complete edited source exactly. |
|
||||
| native-3 01–06 | Fresh process; reopened the working model, returned to System/State view 03, selected Activate. Both authored handles and downward bend persisted. Save produced identical complete source bytes. |
|
||||
| native-3 07–08 | Opened built-in Engineering guide and its States, modes & transitions topic; guide content rendered normally. No external network-disconnection test was performed. |
|
||||
|
||||
`native-check.py` is a read-only full-byte evaluator. It starts with the independently downloaded companion baseline and allows only one exact addition in Activate's waypoint list in readiness_view. Consequently semantics, IDs, node positions, endpoint attachments, labels, other routes and all other views retain their original bytes. Its output is `native-check.json`. The five accepted saved snapshots are retained. A separate source reviewer checks the oracle and key original images in `native-source-review.md`.
|
||||
|
||||
## Scope and observations that are not pass claims
|
||||
|
||||
The filename `native-2/26-second-escape-cancels-form` records the intended probe, not its outcome. Captures 26 and 27 show the form still open. Source review in `picker-policy-review.md` confirms that ordinary State edit drafts use explicit Cancel; #35 retains existing surrounding Escape policies. No second-Escape cancellation or direct inspection of the history stack is claimed. After explicit Cancel, exact source preservation was checked; no fields had been edited in that form.
|
||||
|
||||
At 56% Fit, this fixture's transition labels are small and crowded. That limits readability and remains within the existing label/layout follow-up; this run does not establish broad diagram ergonomics, accessibility, label clarity or aggregate editing acceptance. The fixture intentionally reports 12 model checks (14 objects/9 links); the modeling issues were not fixed as part of a presentation-only check. This exercise does not cover all release-checklist cases; producer and other reviewers retain their separate scopes.
|
||||
|
||||
The first launcher invocation (`native-1`) failed before product startup because the copied environment dictionary used the wrong case for SystemRoot. The helper was corrected to read the case-insensitive original environment; the empty native-1 directory contains no run evidence and is excluded. File-dialog automation needed direct visible Filename entry after UIA set_value was unsupported. On reopening, an undeclared JavaScript local caused one tool call to fail before its action; declaration then succeeded. These are harness/tool errors, not product crashes. Raw successful native-2/native-3 captures and receipts are preserved without renaming or overwriting.
|
||||
|
||||
No release/tag changes or publication are performed by this QA branch. Codex-956atf owns release admission and publication.
|
||||
|
||||
Parent read-only server evidence reconciliation initially used Python's Windows default text decoding for accepted-envelope.json and failed equality on the Unicode comment. Explicit UTF-8 decoding made the saved HTTP/envelope/store comparison pass. No runtime request, product file, or captured source was changed; `server-readback-check.py` and its result preserve the corrected evaluator. The original packaged-server runtime smoke itself passed on its first run.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Independent native source and screenshot review
|
||||
|
||||
2026-09-24. Reviewed captured package-QA evidence in `C:/ar-prerelease-qa-9e8r6t/.local/package-qa`, for the run receipts identifying source `715036e132677dd2bc2fb37ed316d93dc9987aec`. No GUI control, application launch, build, Git operation or captured-evidence modification. This file is the only written artifact. This is bounded review of the State bend/picker/reopen trial, not general release acceptance.
|
||||
|
||||
## Complete-file oracle: PASS
|
||||
|
||||
Baseline: `download-1/companions-21/prerelease-human-checks-20260924/state/native-fixture.arcadia`, 9662 bytes, SHA256 `2dae785d63a940094d8052f3dcddcb988c024b38df9780e026bccd516264dbfe`.
|
||||
|
||||
Using the bundled Python standard library in read-only mode, I compared every byte of each captured file against the original baseline with exactly one expected replacement. The unique replacement target is:
|
||||
|
||||
```text
|
||||
edge @activate; waypoints [{ x 500; y 310; }]
|
||||
```
|
||||
|
||||
The expected replacement retains that original waypoint and appends exactly this new serialized object (LF line breaks):
|
||||
|
||||
```text
|
||||
edge @activate; waypoints [{ x 500; y 310; }, {
|
||||
x X.0;
|
||||
y Y.0;
|
||||
}]
|
||||
```
|
||||
|
||||
The target occurs once, inside `diagram readiness_view`, ID `ea202922-0033-4000-8000-000000000200`. All five full-file comparisons passed, without normalizing whitespace, line endings, strings or numbers. My initial compact single-line serialization expectation differed only in the inserted object's formatting; after explicitly accounting for the actual multiline decimal serialization above, every byte of the complete expected files matched.
|
||||
|
||||
| Captured file | New point | Bytes | SHA256 |
|
||||
| --- | --- | --- | --- |
|
||||
| `native-2/bend-saved.arcadia` | (530,385) | 9705 | `ea05d0e409687c7943dda0b93bc34036eb524f5f5033b912e71ad2eaf643a158` |
|
||||
| `native-2/move-undo-saved.arcadia` | (526,310) | 9705 | `7f1fc18265a79b7d452e4a3de3f17671684e38c0f727cbdec15833196f46f114` |
|
||||
| `native-2/move-redo-saved.arcadia` | (530,385) | 9705 | `ea05d0e409687c7943dda0b93bc34036eb524f5f5033b912e71ad2eaf643a158` |
|
||||
| `native-2/after-cancel-saved.arcadia` | (530,385) | 9705 | `ea05d0e409687c7943dda0b93bc34036eb524f5f5033b912e71ad2eaf643a158` |
|
||||
| `native-3/reopened-saved.arcadia` | (530,385) | 9705 | `ea05d0e409687c7943dda0b93bc34036eb524f5f5033b912e71ad2eaf643a158` |
|
||||
|
||||
Consequently, all semantic declarations, IDs, references, names, comments, transition endpoints/kind/guard/effects, original waypoint, route label/attachments, other routes, nodes and all other views remain byte-for-byte original. The moved, redone, explicit-cancel-saved and fresh-reopen-saved files are mutually byte-identical. This is stronger than checking selected parsed fields; it does not rely on the application's own serializer as the oracle for unrelated content. No semantic parser or hidden model/history introspection was run.
|
||||
|
||||
## Original screenshots inspected
|
||||
|
||||
I opened these full 1605x872 original `-1.jpg` captures using view_image, rather than the 16x16 companion thumbnails:
|
||||
|
||||
- `native-2/09-add-bend-1.jpg`, `10-undo-added-bend-1.jpg`, `11-redo-added-bend-1.jpg`: selected Activate route visibly has two square bend handles, then one, then two; status text correspondingly shows the State update, model change undone, and model change redone. This supports visible add/undo/redo behavior, without proving internal history depth or exact command count.
|
||||
- `native-2/14-undo-bend-move-1.jpg`, `16-redo-bend-move-1.jpg`: the added handle returns beside the original handle on the upper row, then moves below it again; status text shows undone/redone. The exact coordinates and preservation claims come from the full-file oracle, not pixel measurement.
|
||||
- `native-2/19-reference-view-1.jpg`: the picker identifies “04 Readiness — untouched reference”; the selected Activate route has no authored square bends. Other-view preservation is independently established by the source oracle.
|
||||
- `native-2/24-transition-source-picker-1.jpg`: the Edit transition form is present and the source-state dropdown is open. `25-first-escape-picker-only-1.jpg`: dropdown closed, form retained with Ready, Active, External and crew_assigned visible. `26-second-escape-cancels-form-1.jpg`: form is still present, despite that capture's optimistic filename. The retained evidence must not be cited as second-Escape cancellation. Its actual content matches the existing explicit-Cancel policy explained in `picker-policy-review.md`.
|
||||
- `native-2/28-explicit-cancel-1.jpg`: form is closed and the moved route remains. `after-cancel-saved.arcadia` confirms no accepted source change after cancel. This does not directly prove a no-op on internal undo/redo stacks.
|
||||
- `native-3/05-persisted-bend-selected-1.jpg`: the reopened “03 Readiness — edit attachments” view shows both square handles and the lower moved bend, with the Activate transition selected. `native-3/08-state-guide-topic-1.jpg`: Engineering guide is open on “States, modes & transitions” with readable workflow content.
|
||||
|
||||
The route labels remain small and crowded at the captured 56% scale, especially in the reference view. These captures do not establish general diagram readability or accessibility. The evidence remains useful for the bounded interaction/source checks above.
|
||||
|
||||
## Run records and limits
|
||||
|
||||
Read `started.json` and `finished.json` for both runs. They record the same packaged binary/source identity, distinct process IDs (8188 then 6904), separate start times, exitCode 0 and unchanged binary/runtime hashes. The run-3 start follows run-2 finish. I independently read all four stdout/stderr files: each is zero bytes with SHA256 `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`. I did not rehash the executable/runtime or independently observe the Alt-F4 key; normal termination and identity assertions are the retained launcher receipts.
|
||||
|
||||
No material contradiction was found between the saved source, the inspected screen states and the bounded trial description. The one evidence-label correction is capture 26: its filename says cancellation, but the form demonstrably remains. The explicit Cancel and subsequent saved/reopened equality are established separately. This review supports exact saved presentation change, reversible visible outcomes, untouched semantics/other views, picker-only first Escape, explicit form cancellation, and fresh-process persistence. It does not prove internal history depth, every intermediate in-memory state, complete State editing, or broader prerelease/native acceptance.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Downloaded prerelease package provenance review
|
||||
|
||||
2026-09-24. Bounded independent static audit of draft release 7, Studio asset 25 and checksum asset 26, downloaded into `download-1`. No material package-integrity, build-linkage, or static portability defect found. Both packaged executables match the preserved individual Cargo-identified build receipts for exact source 715036e; this review does not establish native acceptance.
|
||||
|
||||
## Reproduction and exact package
|
||||
|
||||
Run `python -B .local/package-qa/package-audit/audit.py` from this worktree. The stdlib audit reuses only the read-only document checker from `scripts/package-artifacts.py`; it does not launch shipped executables, invoke Cargo, or contact a network. Machine-readable results are in `package-audit/result.json`. Initial read-only Git checks reported clean tracked work and HEAD `715036e132677dd2bc2fb37ed316d93dc9987aec`.
|
||||
|
||||
The Studio ZIP is 34,168,661 bytes, SHA256 `5554e32533e3cb2c33fb4143a5a0902733d5a8d47577303cb2842d268e7bc76d`, matching the downloaded checksum and download receipt. All 565 archived files equal their extracted bytes and recorded pins. Names have no traversal, absolute paths, backslash ambiguity, or case-insensitive duplicates; extraction has no extra files. All 564 `SHA256SUMS.txt` entries pass, covering every file except that checksum inventory itself. All 559 `package.json` input hashes pass.
|
||||
|
||||
The supplied BuildEvidence and release snapshot identify source `715036e132677dd2bc2fb37ed316d93dc9987aec`, development profile, Rust 1.98.1, and `x86_64-pc-windows-gnullvm`. Both executable hashes match the actual archive bytes:
|
||||
|
||||
- Studio: `2c03bd6cc3f1110c219e4f57ab94874a121590f83779239a93289b7170664399`.
|
||||
- Server: `6cebacc117e46fdab023c6402a5cebd6929fb55993ae8ff6b89cd1c6c1170237`.
|
||||
|
||||
The four BuildEvidence references (mandatory gate, source manifest, environment, artifact relocation) retain producer-local path labels. Their bytes are independently available in versioned gate evidence linked by the downloaded release snapshot: `206f3b969482241e01e2dfc6be955caf31747b89:harness/prerelease37-windows-gates-20260924/final04-complete`. After the parent fetched those objects, this reviewer read them without checkout or mutation. `package-audit/build-link.py` verifies the complete 6,588,421-byte evidence ZIP (SHA256 `7cead0b6aa36a6af8fc9f8987186bfe0decd8fa2ebab094fc45387ae9daea632`), all 462 member hashes, and exact agreement with all four BuildEvidence pins. Results and pertinent original receipt records are retained in `package-audit/build-link-records.json`.
|
||||
|
||||
Both individual builds have exit 0 and unchanged input captures identical to the final04 source manifest. Their source, target, and profile agree with the package; the preserved output size/hash equals each actual downloaded executable. The relocation records' Cargo compiler-artifact objects exactly occur in the corresponding pinned raw Cargo JSON logs, identify the appropriate non-test binary target and output path, and both streams record successful build completion. The aggregate summary records a passed gate at the same source. This bridges the package to the owner's already admitted source/build evidence; it does not rerun the broader 3,709-input source audit. Supplemental combined-build output hashes differ and are correctly excluded from the package identity claim. Keep the versioned evidence link in the final release so recipients can resolve the historical local labels.
|
||||
|
||||
## Runtime and offline content
|
||||
|
||||
An independent PE32+ parser confirmed x86-64 architecture and reproduced every recorded import for Studio, Server, and `libunwind.dll`. The only ordinary non-system dependency is the shipped `libunwind.dll`, whose own imports resolve to Windows components/API sets. All three delay-import directories are empty. The runtime hash matches package inputs and notice inventory: `aa282fa47bb2c4e210cea5e470cdbdca58568f0be0bc73e404d86ab9cd95ef6a`. Dynamic graphics loading, driver compatibility, and execution without development PATH remain runtime checks owned by the parent; static imports cannot establish them.
|
||||
|
||||
All 18 checked local Markdown/reST/HTML file links resolve. The two HTML specifications load no external script/style/image resources and contain inline styling. All 66 shipped documentation/example/deployment/license source files checked match this exact clean source checkout (README maps to `scripts/package-readme.md`). This includes the complete tracked `examples/release1` content. Built-in Help behavior and actual example opening require the parent's separate native exercise.
|
||||
|
||||
The notice inventory contains 268 dependency packages. All 489 referenced notice paths/hashes match shipped bytes; Cargo.lock and 529 recorded local source/build-input hashes match the exact checkout with no mismatch. Rust and libunwind notices are present and pinned. This verifies the recorded notice closure, not an independent universal license audit, registry recompilation, or proof of embedded asset completeness.
|
||||
|
||||
## Launch guidance and scope
|
||||
|
||||
Read START-HERE, package README, Windows packaging guide, server launch/configuration guidance, and preview human checks. They consistently require keeping the bundle together and the runtime DLL beside both executables; local modeling needs no Rust installation or server. They distinguish packaged executable launch from Cargo/Docker recipes requiring a separate source checkout, and recommend keeping server data outside the application folder. The Windows guide explicitly requires fresh extraction, unrelated working directory, restricted process PATH, actual workflows, logs/exit checks, and acknowledges a second machine as stronger portability evidence.
|
||||
|
||||
The human checklist presents expected exercises rather than claiming those exercises passed. It preserves aggregate deletion and broader acceptance limitations. The downloaded release snapshot remains draft/prerelease with packaged runtime acceptance pending; this audit does not upgrade that state. No GUI/server execution, Cargo, Linux checks, product/source changes, Git mutation, or network activity occurred. Only the requested local review/audit artifacts were written.
|
||||
@@ -0,0 +1,27 @@
|
||||
# State transition picker Escape policy review
|
||||
|
||||
2026-09-24. Read-only source/policy analysis in the package-QA checkout identified by the parent as `715036e`. Read AGENTS.md, PROJECT_GOAL.md (relevant keyboard/cancellation search), `docs/preview-human-checks.md`, `harness/picker-escape-20260923.md`, the combined integration review, and actual State form/App/Escape-wrapper source and relevant tests. No Cargo, Git, GUI or network operations. The native observation (first Escape closes dropdown, second retains State transition form, capture 26) is parent-reported; I did not inspect that capture.
|
||||
|
||||
## Conclusion
|
||||
|
||||
The reported behavior is consistent with the implemented ordinary State Edit transition policy. It is **not evidence of a regression in issue 35**. The current ordinary State draft has an explicit Cancel action; a generic second Escape is not wired to discard it. The checklist does not establish a new universal second-Escape cancellation requirement. This conclusion distinguishes the actual implementation and selected #35 contract from a broader usability preference; it does not claim that every modal's Escape behavior is consistent or ideal.
|
||||
|
||||
## Actual call paths
|
||||
|
||||
- `crates/oa-desktop/src/behavior/states.rs:247-282`: `Event::Edit(id)` constructs the editable transition draft in `states.draft`. The form's From state / To state pickers are at `:454-455`. The word source here denotes the transition endpoint; this is not a nested behavior source-authoring form.
|
||||
- `states.rs:283` explicitly handles `Event::Cancel` by clearing the draft through acknowledge_commit. The visible Cancel button emits that event at `:576`. `behavior.rs:938-946` recognizes cancellation of the currently pending State draft and acknowledges it without returning a model command. The independent behavior-form test also checks that explicit Cancel discards the pending form (`behavior.rs:3560`).
|
||||
- `behavior.rs:908-914` defines `source_active` from creation, Scenario and Process source forms; it does not include an ordinary State transition edit. `creation.has_pending()` is a separate editor from `states.draft`.
|
||||
- `app.rs:2742-2760` handles generic Message::Escape: source_back first, then source_active cancellation, then behavior.creation cancellation, then State AdjustAttachment tool cancellation. It has no ordinary State draft cancellation branch. If an uncaptured second Escape reaches this handler, it can retire transient canvas state, but it does not clear the transition draft or commit model/source/history changes.
|
||||
- The #35 root wrapper (`app/escape.rs:18-32`) forwards captured focused-input Escape only for the existing listed workflows: behavior.creation, diagram usages, behavior source forms and specified Text workflows, plus the separate architecture callback. It does not add ordinary State transition forms to that policy. Its popup preflight lets an open dropdown consume Escape before those surrounding actions (`:101-140`). The global subscription separately ignores captured Escape (`app.rs:1316-1320`). Thus both captured and uncaptured paths are compatible with the reported second-Escape retention.
|
||||
|
||||
## Contract and test limits
|
||||
|
||||
`harness/picker-escape-20260923.md:10-15` explicitly requires popup dismissal without changing the surrounding draft/model/source/history/selection/camera, followed by the existing surrounding workflow's policy on a later Escape. Its design preserves existing activation predicates and message priorities. The combined review repeats “next Escape follows existing modal priority” (`harness/reviews/prerelease37-integration-20260924.md:38`).
|
||||
|
||||
`docs/preview-human-checks.md:45-47` says a subsequent Escape follows the form's ordinary cancellation behavior. It does not say every form closes on that key. Read against the actual selected #35 contract, it should not be used to invent a second-Escape cancellation oracle for this State editor. If polishing the checklist later, “follows the surrounding workflow's existing Escape policy; use explicit Cancel where that form requires it” would remove the ambiguity without changing behavior.
|
||||
|
||||
`app/state_picker_escape_tests.rs` exercises the actual State missing-element picker, including close/reopen/place and exact unchanged document/selection/camera/history on dismissal; it is not a test that second Escape closes the transition editor. The composed pending-form cases in `app/escape/tests.rs` use behavior.creation/source workflows, whose existing Escape policy does cancel or go back. Those test results should not be generalized to ordinary State editing.
|
||||
|
||||
## Recommended package-QA disposition
|
||||
|
||||
Record first-Escape popup dismissal with unchanged transition form as the observed #35 success for this native control. Record second-Escape form retention as compatible with its current explicit-Cancel path, rather than a product regression inferred from the generic checklist. Use the visible Cancel button, verify the form closes and accepted model/source/history remain unchanged, and continue package QA. A future request to make ordinary State forms Escape-cancelable would be a separate product-policy change requiring consistent draft-loss/focus semantics and its own verification.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Independent packaged server review — draft release 7
|
||||
|
||||
**PASS for the bounded Windows packaged-server HTTP/restart smoke.** 23 explicit checks passed on Windows 11 build 26200. The helper exited 0. This is runtime evidence for the downloaded packaged server, not a source build, native desktop acceptance, or release publication.
|
||||
|
||||
## Identity and execution isolation
|
||||
|
||||
- Reviewed source identity supplied by package build evidence: `715036e132677dd2bc2fb37ed316d93dc9987aec`. Package identifies `dev` profile, `x86_64-pc-windows-gnullvm`, rustc `1.98.1 (48a229cea 2026-09-01)`. These source/compiler/profile statements remain distributor assertions, not independently rebuilt provenance.
|
||||
- Downloaded Studio ZIP: `ArcadiaStudio-0.2.0-preview.integrated.20260924-x86_64-pc-windows-gnullvm.zip`, 34,168,661 bytes, independently rehashed SHA-256 `5554e32533e3cb2c33fb4143a5a0902733d5a8d47577303cb2842d268e7bc76d`.
|
||||
- Executed only its extracted `arcadia-server.exe`, SHA-256 `6cebacc117e46fdab023c6402a5cebd6929fb55993ae8ff6b89cd1c6c1170237`. `--version` returned `arcadia-server 0.2.0`; version/help exited 0. Bundled `libunwind.dll` hash `aa282fa47bb2c4e210cea5e470cdbdca58568f0be0bc73e404d86ab9cd95ef6a` is recorded.
|
||||
- Child environment is an explicit Windows runtime whitelist, with PATH exactly `C:\Windows\System32;C:\Windows;C:\Windows\System32\Wbem`. No inherited Rust/development PATH or server bearer token. Python orchestrates HTTP and process lifetime only; it supplies no server runtime library.
|
||||
- Working directory and fresh isolated store are under `.local/package-qa/server-smoke`; original extracted package files were not modified. Listener used newly available loopback port `127.0.0.1:61860`, never a non-loopback interface. No existing store or service was touched.
|
||||
|
||||
Literal helper launch:
|
||||
|
||||
```powershell
|
||||
& C:/Users/User/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/python.exe .local/package-qa/server-smoke/run.py
|
||||
```
|
||||
|
||||
Exact child argv arrays, environment, executable/runtime hashes, process IDs and exit results are in [server-smoke/result.json](server-smoke/result.json). The helper refuses a preexisting store, so reruns must use a fresh evidence directory rather than overwriting this run.
|
||||
|
||||
## Exercised behavior
|
||||
|
||||
1. Startup readiness returned 200. Served OpenAPI returned 200 and exactly equalled the packaged OpenAPI JSON contract.
|
||||
2. Imported the complete authored source bundle with stable model/activity IDs, an explicit Unicode comment and CRLF text. POST returned 201; GET preserved the entry filename and exact file text, UUIDs and comment. Server resource ID was independently allocated and distinct from the model ID.
|
||||
3. GET returned the same initial strong ETag; conditional source-project PUT changed only project name `Package QA original` to `Package QA edited`, returned changed=true, incremented revision exactly from 1 to 2 and returned a new ETag plus exact edited bundle.
|
||||
4. A second PUT with the stale revision-1 ETag and a distinctive rejected name returned `412 revision_conflict`. Subsequent GET equalled the accepted edited model/source pair exactly; the expected model was independently constructed by changing only its name in the original model.
|
||||
5. The acknowledged HTTP envelope exactly equalled the stored JSON envelope. The server was terminated, restarted from the same store and again passed readiness. Whole-envelope GET and source-project GET retained exact revision-2 ETag, all IDs, name, comment and CRLF text. Restart/readback did not change committed envelope bytes.
|
||||
|
||||
Resource: `230d7234-8150-4252-a4bd-85a3b7b2308c`; final revision 2; store SHA-256 `c928995daf0c1ff3963850aba9ad533196423fa2112ad99554758825f73fc5af`.
|
||||
|
||||
## Evidence and limits
|
||||
|
||||
[server-smoke/run.py](server-smoke/run.py) is the executable evaluator. Numbered `*.request.http` and `*.response.http` files contain literal requests/responses, including source bodies and ETags, without secrets. `original.arcadia`, `edited.arcadia`, `accepted-envelope.json`, isolated `store/`, copied `package-provenance.json`, help/version output and both process stdout/stderr logs are retained. Logs contain normal startup messages; no crash/error diagnostics were observed.
|
||||
|
||||
Both server processes were deliberately stopped with Windows TerminateProcess after completed, acknowledged requests (exit code 1 is the imposed termination result). This verifies restart recovery of acknowledged writes; **it does not establish graceful Ctrl+C shutdown, in-flight-write recovery or power-loss safety**. Both processes have been stopped. Authentication, remote networking/TLS, Linux/container operation, broad API semantics, concurrency/load and GUI behavior are outside this bounded run. No Cargo/build, source edits, Git mutations, Gitea messages or GUI operations occurred.
|
||||
@@ -1,87 +0,0 @@
|
||||
# Projection switching and canvas inspection
|
||||
|
||||
Branch contribution for [#42](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/42)
|
||||
and [#43](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/43),
|
||||
based on published preview `715036e132677dd2bc2fb37ed316d93dc9987aec`.
|
||||
Owner: Codex-ytr83r, session 01a0d706. This is a candidate, not integrated
|
||||
acceptance or a replacement release. Live status belongs to the linked issues.
|
||||
|
||||
## Decision and alternatives
|
||||
|
||||
String projections are presentation filters over one authored diagram. Preserve
|
||||
opposite-layer placements, routes and visibility selections when switching away
|
||||
from Traceability; ordinary projections render only their declared layer. This
|
||||
extends the existing documented preservation of inapplicable element kinds.
|
||||
Deleting incompatible placements loses authored arrangements; automatically
|
||||
creating a second view fragments the user's work and makes repeated switching
|
||||
ambiguous. Neither alternative satisfies reversible view switching as directly.
|
||||
|
||||
Missing identities and wrong reference kinds/families remain errors. StateMachine
|
||||
and Scenario keep strict typed membership. Frozen versions 1–6 retain their old
|
||||
layer admission before migration. The current version-7 correction adds no fields
|
||||
or wire shapes; older version-7 readers may reject newly admitted latent records.
|
||||
That compatibility limit must remain explicit until coordinated version adoption.
|
||||
The explicit RelocateNode operation continues to require a compatible destination
|
||||
layer or Traceability view; retaining latent records is distinct from moving a
|
||||
placement into an incompatible destination.
|
||||
|
||||
Canvas inspection carries the originating route context and preserves view/layer.
|
||||
Explicit model navigation continues to follow the target layer. Draft validation
|
||||
and stale-context rejection apply before inspection. Existing production group
|
||||
selection already retained the canvas; fallback edge/port selection and canvas
|
||||
context actions require the correction. The published Windows baseline reproduces both reported failure states;
|
||||
source diagnosis and those baseline results do not establish patched acceptance.
|
||||
Contextual child/relationship creation must derive the semantic layer from the
|
||||
selected object, while root creation continues to use the active perspective.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- OA and SA switches through all ordinary projections preserve the complete model
|
||||
except projection, source/JSON/bundle round trips, and one-step Undo/Redo.
|
||||
- Returning to Traceability restores applicable placements; parent, port and edge
|
||||
projection never leaks an opposite-layer record into an ordinary view.
|
||||
- Invalid references and State/Scenario membership still reject; source and JSON
|
||||
versions 3–6 retain their old layer checks and located diagnostic identities.
|
||||
- Actual canvas clicks, context Properties/Hide and explicit navigation exercise
|
||||
the intended selection distinction; stale packets and invalid drafts preserve
|
||||
selection and source/history.
|
||||
- Windows native selection, projection switching, Save, Undo/Redo and fresh reopen
|
||||
are checked against predeclared full-model/source expectations. Compilation and
|
||||
automated handler tests do not substitute for this acceptance.
|
||||
|
||||
Linux remains excluded by the existing user pause. Independent design critique
|
||||
identified saved visibility selectors and frozen migration as additional admission
|
||||
paths; both are included. At published candidate `603a59c1785dbcca324dae063ecc3405d9519a30`, 41 targeted core checks pass: legacy admission (2), projection switching (3), review regressions (15), source-6 view contract (8), and state diagrams (13). Tracked inputs were unchanged across the run. Whole-workspace formatting also passed at that commit. The first `0341bca` core attempt failed a generated-UUID test oracle; explicit fixture identities corrected the evaluator, without relaxing the admission assertion.
|
||||
|
||||
The desktop attempt at `603a59c` was deliberately interrupted before test execution after independent source review found canvas Properties redispatch and contextual relationship/child semantic-layer defects. Its source inputs remained unchanged; no desktop passing result is claimed. The Properties issue arose from fixing explicit navigation to an already-inspected opposite-layer object. Keep both regressions: contextual Properties stays in the view, explicit navigation follows the object's layer.
|
||||
|
||||
Published-preview executable SHA256 `2c03bd6cc3f1110c219e4f57ab94874a121590f83779239a93289b7170664399` reproduces edge selection changing SA to OA, and Architecture switching refused with `diagram_layer`. The isolated valid fixture remained byte-for-byte unchanged after the trial. An earlier fixture used reserved alias `capability` and was rejected; it was corrected before either reproduction. Initial native-dialog capture/input failures also remain separate from application acceptance. Independent evidence review confirmed the resulting screenshot states and persisted byte equality; screenshots alone do not capture the complete action sequence or transient state history.
|
||||
|
||||
The frozen full-workspace attempt at `3a7221b80cfa7924921b3b77f2dd35550247f595`
|
||||
closed with unchanged tracked inputs and Cargo exit101: core/CLI groups passed616
|
||||
with1 existing ignore; desktop passed1538, failed1 and retained7 existing ignores.
|
||||
The sole failure was the new preferred-view test's setup, before child creation:
|
||||
`Message::Projection` edits and selects the diagram, so the test's expectation
|
||||
that the parent remained selected was false. An exact isolated rerun and source
|
||||
review confirmed this. The correction initializes the projection in the fixture,
|
||||
then uses explicit parent selection and the ordinary diagram picker to establish
|
||||
independent semantic inspection. Production navigation is unchanged. All other
|
||||
new canvas tests passed, including Properties, explicit navigation, relationship
|
||||
Apply/history, child/root semantic layer, and projection/source/history checks.
|
||||
The command remains a failed workspace run; server tests and doctests were not
|
||||
reached. Its planned Clippy/build stages were consequently not launched.
|
||||
|
||||
The same source's formatting check found one multiline icon call requiring a
|
||||
single line. The follow-up applies that formatting correction and adds in-app
|
||||
help describing preserved cross-layer content and contextual inspection. Those
|
||||
are the only production-file changes after the full suite; the remaining change
|
||||
corrects the test setup. Follow-up canvas/server/doctest, formatting, strict
|
||||
Clippy and native-build results must be recorded separately, with their exact
|
||||
source pin. Reuse successful unchanged core/desktop evidence within these limits;
|
||||
do not turn the failed workspace command into a pass.
|
||||
|
||||
Candidate Windows GUI save/history/reopen, HTTP regression, the corrected test
|
||||
and final checks remain outstanding. Raw local evidence is not yet a
|
||||
peer-consumable handoff; publish the consolidated evidence before integration.
|
||||
|
||||
Scope boundary: contextual relationship forms retain the canvas while opening; successful Apply retains the existing explicit post-review navigation. The generic child-creation handler is tested for containment and allocation, but the separate architecture palette/performer workflow still offers creation kinds for the diagram layer. Thus an OA performer displayed in an SA Traceability view has no Add activity menu entry; this inherited limitation is not claimed repaired here.
|
||||
Reference in New Issue
Block a user