Compare commits

...
Author SHA1 Message Date
sorokin.oleg.edu 9c79a6e0fb Verify Help preserves mixed-view caret and retires held Draw gestures 2026-10-02 00:45:40 -07:00
sorokin.oleg.edu 8e5b299e6e Merge verified navigation with delivered Help and Save 2026-10-02 00:28:17 -07:00
sorokin.oleg.edu d21977fca6 docs: publish verified navigation source and portable evidence 2026-10-02 00:20:04 -07:00
sorokin.oleg.edu 6018ae30b9 Follow accepted placement relocation without losing inspection context 2026-10-01 23:42:47 -07:00
sorokin.oleg.edu c7065db76e Preserve canvas context across property edits and contextual drawing 2026-10-01 22:49:47 -07:00
sorokin.oleg.edu bc69c6d449 docs: confirm save diagnostics preview publication and handoff 2026-10-01 20:32:01 -07:00
sorokin.oleg.edu e144e32e99 docs: preserve verified save diagnostics package and provenance 2026-10-01 20:24:08 -07:00
sorokin.oleg.edu ef28f1784e Refresh published Save As contribution checkpoint 2026-10-01 18:55:30 -07:00
sorokin.oleg.edu 8e59309cbc Bring published compact Help evidence into Save As contribution 2026-10-01 18:54:56 -07:00
sorokin.oleg.edu 6ea7518e7b Link confirmed compact Help preview and exact asset readbacks 2026-10-01 18:24:37 -07:00
sorokin.oleg.edu f1fdca3713 Record exact compact Help package acceptance and independent review 2026-10-01 18:20:30 -07:00
Codex-ytr83r 6520a89dc0 test: establish independent inspection before preferred-view creation 2026-09-25 00:26:42 -07:00
Codex-ytr83r 3a7221b80c Distinguish connection tools with semantic palette icons 2026-09-24 23:31:02 -07:00
Codex-ytr83r ac69f555af Keep contextual properties and creation bound to their semantic targets 2026-09-24 23:30:56 -07:00
Codex-ytr83r 603a59c178 Verify projection API and preserve same-object explicit navigation 2026-09-24 23:00:05 -07:00
Codex-ytr83r 0341bca846 Preserve authored projection content and contextual canvas selection 2026-09-24 22:40:21 -07:00
1010 changed files with 317610 additions and 141 deletions
+5
View File
@@ -1,4 +1,5 @@
* text=auto eol=lf
*.rs text eol=lf
*.toml text eol=lf
*.lock text eol=lf
@@ -89,3 +90,7 @@ harness/experiments/analysis-source-s2a-windows-20260913/** -text whitespace=-bl
# Preserve the pinned Iced dependency bytes and upstream manifest.
crates/vendor/iced_widget/** -text whitespace=cr-at-eol
# Final overrides preserve original validation bytes after generic text rules.
harness/experiments/navigation-validation-20261002/** -text whitespace=-blank-at-eol,-blank-at-eof,cr-at-eol
harness/reviews/navigation-validation-packet-20261002/** -text whitespace=-blank-at-eol,-blank-at-eof,cr-at-eol
+60 -6
View File
@@ -340,6 +340,18 @@ 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()
@@ -622,6 +634,7 @@ 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,
@@ -629,8 +642,8 @@ fn parse_document(source: &str, limits: ParseLimits) -> Result<ParsedDocument, V
value_depth: 0,
limits,
}
.document()
.map_err(|e| vec![diagnostic(source, e)])
.document(&mut migration_issue)
.map_err(|e| vec![diagnostic_with_issue(source, e, migration_issue)])
}
fn offset_for(source: &str, line: usize, column: usize) -> usize {
let mut offset = 0;
@@ -1449,7 +1462,10 @@ impl Parser<'_> {
},
}
}
fn document(&mut self) -> Result<ParsedDocument, Error> {
fn document(
&mut self,
migration_issue: &mut Option<validation::Issue>,
) -> 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)]);
@@ -1476,9 +1492,14 @@ impl Parser<'_> {
.map(|(key, span)| ((d.id, key.clone()), span.start)),
);
}
let decoded =
resolve_declarations(header.project, &unit.declarations, &aliases, self.limits)
.map_err(|(_, error)| error)?;
let decoded = resolve_declarations(
header.project,
&unit.declarations,
&aliases,
self.limits,
migration_issue,
)
.map_err(|(_, error)| error)?;
Ok(ParsedDocument {
project: decoded.project,
migration: decoded.migration,
@@ -1682,6 +1703,7 @@ 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();
@@ -1989,6 +2011,38 @@ 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 {
+9 -3
View File
@@ -69,8 +69,8 @@ impl Project4 {
}
crate::persistence::check_pre6_relations(self.relations.values())?;
crate::persistence::check_pre6_elements(self.elements.values())?;
Ok(Project {
schema_version: SCHEMA_VERSION,
let mut project = Project {
schema_version: self.schema_version,
id: self.id,
alias: self.alias,
name: self.name,
@@ -84,7 +84,11 @@ 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)
}
}
@@ -104,6 +108,8 @@ 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;
+46
View File
@@ -341,6 +341,50 @@ 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,
@@ -348,6 +392,7 @@ 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
@@ -420,6 +465,7 @@ 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;
+1
View File
@@ -85,6 +85,7 @@ 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()
+11
View File
@@ -589,6 +589,17 @@ 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)
+20 -10
View File
@@ -573,16 +573,26 @@ fn compile_once(
span,
}),
};
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 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 diagnostics = model_diagnostics(bundle, &index, &decoded.project);
let errors: Vec<_> = diagnostics
.iter()
+12 -7
View File
@@ -620,11 +620,14 @@ 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);
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)
};
// 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)
};
for issue in option_issues {
error(&mut out, d.id, "view_options", issue.field, issue.message);
}
@@ -798,8 +801,10 @@ pub fn validate(p: &Project) -> Vec<Issue> {
);
}
match p.elements.get(&n.element) {
Some(e) if d.projection == ProjectionKind::Traceability || e.layer() == d.layer => {
}
Some(e)
if (d.projection.state_machine().is_none()
&& d.projection.scenario().is_none())
|| e.layer() == d.layer => {}
_ => error(
&mut out,
n.id,
+3 -1
View File
@@ -218,7 +218,9 @@ pub fn validate_options(p: &Project, layer: Layer, options: &ViewOptions) -> Vec
validate_options_in(p, Some(layer), options)
}
/// Traceability views admit references from either architecture layer.
/// 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.
pub fn validate_options_cross_layer(p: &Project, options: &ViewOptions) -> Vec<OptionError> {
validate_options_in(p, None, options)
}
@@ -0,0 +1,205 @@
//! 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(&current_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;
}
}
@@ -0,0 +1,304 @@
//! Populated presentation survives mixed-layer projection switches.
//! Complements projection_switching.rs without external fixture dependencies.
use oa_core::{
projection::*, routing::route_diagram, source_project::*, view_options::ViewIndex, *,
};
struct Populated {
project: Project,
diagram: Id,
groups: [Id; 2],
children: [[Id; 2]; 2],
edges: [Id; 2],
ports: [Id; 2],
trace: Id,
}
fn xy(x: f32, y: f32) -> Position {
Position { x, y }
}
fn apply(project: &mut Project, command: Command) {
*project = prepare_command(project, &command).unwrap().project;
}
fn fixture(layer: Layer, collapsed: bool) -> Populated {
let mut p = Project::empty();
let oa_group = p
.add_element(Kind::Activity, Layer::Oa, "Operational response", None)
.unwrap();
let oa_send = p
.add_element(Kind::Activity, Layer::Oa, "Request repair", Some(oa_group))
.unwrap();
let oa_receive = p
.add_element(Kind::Activity, Layer::Oa, "Perform repair", Some(oa_group))
.unwrap();
let sa_group = p
.add_element(Kind::Function, Layer::Sa, "System response", None)
.unwrap();
let sa_send = p
.add_element(Kind::Function, Layer::Sa, "Dispatch repair", Some(sa_group))
.unwrap();
let sa_receive = p
.add_element(Kind::Function, Layer::Sa, "Execute repair", Some(sa_group))
.unwrap();
let oa_edge = p.relate(
RelationKind::Interaction,
"Repair request",
oa_send,
oa_receive,
);
let sa_edge = p.relate(
RelationKind::FunctionalExchange,
"Repair dispatch",
sa_send,
sa_receive,
);
let RelationBody::FunctionalExchange {
source_port,
target_port,
..
} = p.relations[&sa_edge].body
else {
unreachable!()
};
let trace = Id::new_v4();
let refinement = Trace {
id: trace,
alias: p.fresh_alias("dispatch_refinement", "trace", trace),
name: "Dispatch refines repair request".into(),
description: "Keep this cross-layer trace and its authored route.".into(),
oa: oa_send,
sa: sa_send,
kind: TraceKind::Refinement,
baseline: Some(TraceBaseline {
oa: p.properties(oa_send).unwrap(),
sa: p.properties(sa_send).unwrap(),
semantic: None,
}),
};
apply(&mut p, Command::CreateTrace(refinement));
let d = p.ensure_diagram(layer);
p.diagrams.get_mut(&d).unwrap().projection = ProjectionKind::Traceability;
let oa_group_node = p.place(d, oa_group, xy(0., 0.));
let oa_send_node = p.place(d, oa_send, xy(40., 120.));
let oa_receive_node = p.place(d, oa_receive, xy(360., 120.));
let sa_group_node = p.place(d, sa_group, xy(0., 500.));
let sa_send_node = p.place(d, sa_send, xy(40., 620.));
let sa_receive_node = p.place(d, sa_receive, xy(360., 620.));
for (node, port, side, offset) in [
(sa_send_node, source_port, PortAnchorSide::Bottom, 0.25),
(sa_receive_node, target_port, PortAnchorSide::Top, 0.75),
] {
apply(
&mut p,
Command::SetPortAnchor {
diagram: d,
node,
anchor: PortAnchor { port, side, offset },
},
);
}
for (edge, y, port_backed) in [
(oa_edge, 260., false),
(sa_edge, 860., true),
(trace, 420., false),
] {
apply(
&mut p,
Command::SetEdgeRoute {
diagram: d,
route: EdgeRoute {
edge,
source_attachment: (!port_backed).then_some(EdgeAttachment {
side: PortAnchorSide::Right,
offset: 0.3,
}),
target_attachment: (!port_backed).then_some(EdgeAttachment {
side: PortAnchorSide::Left,
offset: 0.7,
}),
waypoints: vec![xy(680., y), xy(740., y)],
label: Some(EdgeLabel {
fraction: 0.4,
offset: xy(13., -27.),
}),
},
},
);
}
for node in [oa_group_node, sa_group_node] {
let mut placement = p.diagrams[&d].nodes[&node].clone();
placement.collapsed = collapsed;
placement.size = Size {
width: 310.,
height: 170.,
};
apply(
&mut p,
Command::EditNode {
diagram: d,
original: node,
node: placement,
},
);
}
validate_candidate(&p).unwrap();
Populated {
project: p,
diagram: d,
groups: [oa_group_node, sa_group_node],
children: [
[oa_send_node, oa_receive_node],
[sa_send_node, sa_receive_node],
],
edges: [oa_edge, sa_edge],
ports: [source_port, target_port],
trace,
}
}
fn assert_round_trips(p: &Project) {
assert_eq!(language::parse(&language::format(p)).unwrap(), *p);
assert_eq!(
persistence::decode(&serde_json::to_vec(p).unwrap()).unwrap(),
*p
);
assert_eq!(
compile_bundle(&format_bundle(p), ProjectLimits::default())
.unwrap()
.decoded
.project,
*p
);
}
fn assert_layer_only(p: &Project, d: Id, view: &ProjectedView) {
let layer = p.diagrams[&d].layer;
for node in &view.nodes {
assert_eq!(p.elements[&node.element].layer(), layer);
if let Some(parent) = node.visual_parent {
let parent = view.nodes.iter().find(|n| n.id == parent).unwrap();
assert_eq!(p.elements[&parent.element].layer(), layer);
}
}
for port in &view.ports {
assert_eq!(p.elements[&port.element].layer(), layer);
let owner = view.nodes.iter().find(|n| n.id == port.owner).unwrap();
assert_eq!(p.elements[&owner.element].layer(), layer);
}
for edge in &view.edges {
assert!(
!edge.trace,
"cross-layer trace leaked into ordinary projection"
);
assert_eq!(p.layer_of(edge.id), Some(layer));
for endpoint in [edge.source, edge.target] {
let owner = view.nodes.iter().find(|n| n.id == endpoint).unwrap();
assert_eq!(p.elements[&owner.element].layer(), layer);
}
for port in [edge.source_port, edge.target_port].into_iter().flatten() {
assert_eq!(p.elements[&port].layer(), layer);
}
}
}
#[test]
fn populated_mixed_projection_switch_conserves_authorship_and_restores_geometry() {
for layer in [Layer::Oa, Layer::Sa] {
for collapsed in [false, true] {
let f = fixture(layer, collapsed);
let baseline = &f.project;
let d = f.diagram;
let before_view = project_view(baseline, d);
let before_routes = route_diagram(baseline, &baseline.diagrams[&d], &before_view);
// Guard against a vacuous fixture: Traceability deliberately flattens parents.
assert_eq!(before_view.nodes.len(), 6);
assert!(before_view.nodes.iter().all(|n| n.visual_parent.is_none()));
for id in [f.edges[0], f.edges[1], f.trace] {
assert!(before_view.edges.iter().any(|e| e.id == id));
let route = baseline.diagrams[&d]
.routes
.iter()
.find(|r| r.edge == id)
.unwrap();
assert_eq!(route.waypoints.len(), 2);
assert!(route.label.is_some());
}
for port in f.ports {
assert!(before_view.ports.iter().any(|p| p.element == port));
}
assert_round_trips(baseline);
for projection in [
ProjectionKind::Overview,
ProjectionKind::Decomposition,
ProjectionKind::Allocation,
ProjectionKind::FunctionalDataflow,
ProjectionKind::Capabilities,
] {
let mut expected = baseline.clone();
expected.diagrams.get_mut(&d).unwrap().projection = projection;
let mut session = Session::new(baseline.clone());
session
.execute(Command::EditDiagram(d, expected.diagrams[&d].clone()))
.unwrap();
assert_eq!(session.project(), &expected);
assert_round_trips(session.project());
let view = project_view(session.project(), d);
assert_eq!(
view,
project_view_with_index(&expected, d, &ViewIndex::new(&expected))
);
assert_layer_only(session.project(), d, &view);
if projection == ProjectionKind::Overview {
let own = if layer == Layer::Oa { 0 } else { 1 };
assert!(view.nodes.iter().any(|n| n.id == f.groups[own]));
for child in f.children[own] {
if collapsed {
assert!(!view.nodes.iter().any(|n| n.id == child));
} else {
assert_eq!(
view.nodes
.iter()
.find(|n| n.id == child)
.unwrap()
.visual_parent,
Some(f.groups[own])
);
}
}
if !collapsed {
assert!(view.edges.iter().any(|e| e.id == f.edges[own]));
if layer == Layer::Sa {
assert_eq!(view.ports.len(), 2);
}
}
}
session.undo();
assert_eq!(session.project(), baseline);
assert!(
!session.can_undo(),
"projection change must be one model history step"
);
assert_round_trips(session.project());
session.redo();
assert_eq!(session.project(), &expected);
session
.execute(Command::EditDiagram(d, baseline.diagrams[&d].clone()))
.unwrap();
assert_eq!(session.project(), baseline);
let restored = project_view(session.project(), d);
assert_eq!(restored, before_view);
assert_eq!(
route_diagram(
session.project(),
&session.project().diagrams[&d],
&restored
),
before_routes
);
assert_round_trips(session.project());
}
}
}
}
@@ -0,0 +1,156 @@
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());
}
+12 -1
View File
@@ -67,7 +67,18 @@ 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;
assert!(validate_candidate(&p).is_err());
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]);
p.diagrams.get_mut(&d).unwrap().projection = ProjectionKind::Traceability;
p.diagrams.get_mut(&d).unwrap().view_options.relation_kinds = RelationKinds::Only(vec![]);
assert!(
+208 -42
View File
@@ -4,8 +4,12 @@ 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 connection_begin_regressions;
#[cfg(test)]
mod context_help_tests;
#[cfg(test)]
mod diagram_focus_tests;
@@ -21,6 +25,8 @@ mod hosted;
pub(crate) mod multiselect;
#[cfg(test)]
mod multiselect_tests;
#[cfg(test)]
mod placement_relocation_regressions;
mod reference_authoring;
#[cfg(test)]
mod review_navigation_tests;
@@ -387,6 +393,7 @@ pub enum Message {
Behavior(crate::behavior::Event),
Layer(Layer),
Select(Id),
CanvasSelect(crate::route_edit::Context, Id),
EditProperties(Id),
SelectNode(Id),
DiagramSelection(multiselect::Event),
@@ -955,6 +962,16 @@ impl App {
return true;
}
let original = self.selected;
let canvas_surface = self.architecture_surface();
let placement_owner = self.draft.as_ref().and_then(|draft| match draft.kind {
crate::forms::RecordKind::Node(owner) => self
.project()
.diagrams
.get(&owner)
.filter(|diagram| diagram.nodes.contains_key(&draft.original))
.map(|diagram| diagram.id),
_ => None,
});
let result = self
.draft
.as_ref()
@@ -1009,13 +1026,16 @@ impl App {
if let Some(layer) = self.project().layer_of(id) {
self.layer = layer;
}
if let Some((diagram, layer, node)) = self
let selected_surface = self
.project()
.diagrams
.values()
.find(|d| d.id == id || d.nodes.contains_key(&id))
.map(|d| (d.id, d.layer, d.nodes.contains_key(&id).then_some(id)))
{
.map(|d| (d.id, d.layer, d.nodes.contains_key(&id).then_some(id)));
let relocated_placement = placement_owner
.zip(selected_surface.and_then(|(diagram, _, node)| node.map(|_| diagram)))
.is_some_and(|(before, after)| before != after);
if let Some((diagram, layer, node)) = selected_surface {
self.layer = layer;
if self.page == Page::Behavior
&& let Some(machine) =
@@ -1029,6 +1049,20 @@ impl App {
self.selected_node = node;
}
}
// Ordinary Properties edits retain their saved architecture view.
// Follow an explicitly edited diagram identity/layer, never an
// inferred replacement for an unrelated queued canvas click.
// An accepted placement move explicitly chooses its new view.
// Same-owner edits (including a new UUID) still retain inspection.
if !relocated_placement && let Some((diagram, _, _)) = canvas_surface {
let diagram = if diagram == original { id } else { diagram };
if let Some(view) = self.project().diagrams.get(&diagram)
&& view.projection.is_architecture()
{
self.layer = view.layer;
self.active_diagram = Some(diagram);
}
}
self.sync();
self.status = "Properties applied".into();
true
@@ -1179,7 +1213,13 @@ impl App {
if id != self.selected {
self.selected_attachment = None;
}
if self.selected == id && !process_preview {
if self.selected == id
&& !process_preview
&& self
.project()
.layer_of(id)
.is_none_or(|layer| layer == self.layer)
{
return;
}
if !self.apply() {
@@ -1225,6 +1265,59 @@ impl App {
}
self.refresh_draft();
}
/// The saved architecture surface, independent of the Inspector's object.
fn architecture_surface(&self) -> Option<(Id, Layer, ProjectionKind)> {
if self.page != Page::Diagram {
return None;
}
let diagram = self.project().diagrams.get(&self.active_diagram?)?;
diagram.projection.is_architecture().then_some((
diagram.id,
diagram.layer,
diagram.projection,
))
}
fn canvas_target_visible(&self, diagram: Id, id: Id) -> bool {
let view = project_view(self.project(), diagram);
view.nodes
.iter()
.any(|node| node.id == id || node.element == id)
|| view.ports.iter().any(|port| port.element == id)
|| view.edges.iter().any(|edge| edge.id == id)
}
/// Inspect a contextual target without following its model layer. An edit
/// may invalidate a previously visible occurrence; accept the edit but
/// refuse that old hit. Invisible Show-in-view targets remain intentional.
fn inspect_canvas_target(&mut self, id: Id) -> bool {
if self.selected == id {
return true;
}
let surface = self.architecture_surface();
let accepted_draft = self.draft_dirty();
let was_visible = accepted_draft
&& surface.is_some_and(|(diagram, _, _)| self.canvas_target_visible(diagram, id));
if !self.project().contains(id) || !self.apply() {
return false;
}
if !self.project().contains(id)
|| (was_visible
&& (self.architecture_surface() != surface
|| !surface
.is_some_and(|(diagram, _, _)| self.canvas_target_visible(diagram, id))))
{
if accepted_draft {
self.status =
"Properties applied. The view changed; select a visible target again.".into();
}
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);
@@ -1425,6 +1518,11 @@ 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, ..)
@@ -2185,6 +2283,7 @@ 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
@@ -2785,10 +2884,26 @@ impl App {
e.kind() != Kind::Port && d.projection.includes(e.kind())
})
});
if show_in_mixed_view {
self.selected = menu.request.object;
self.selected_attachment = None;
self.refresh_draft();
// Draw acts on the visible current view even when opened from
// Explorer; explicit Select/Open actions still navigate.
let draw_in_current_view = match &*action {
Message::Connection(crate::connection_edit::Event::Begin(tool, source)) => {
*source == menu.request.object
&& self.active_diagram.is_some_and(|diagram| {
crate::connection_edit::can_begin(
self.project(),
diagram,
*tool,
*source,
)
})
}
_ => false,
};
if show_in_mixed_view || draw_in_current_view || menu.request.canvas.is_some() {
if !self.inspect_canvas_target(menu.request.object) {
return Task::none();
}
} else {
self.select(menu.request.object);
}
@@ -2800,7 +2915,15 @@ impl App {
}
self.reconcile_multiselect();
self.selected_bend = menu.request.bend.map(|i| (menu.request.object, i));
return self.update(*action);
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);
}
Message::CreateContextElement(kind, child) => {
self.create_kind = kind;
@@ -2985,6 +3108,14 @@ 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);
@@ -3004,10 +3135,23 @@ impl App {
return Task::none();
}
let original = self.selected;
let surface = self.architecture_surface();
let accepted_draft = self.draft_dirty();
if !self.apply() {
return Task::none();
}
let id = if id == original { self.selected } else { id };
if let Some((diagram, _, _)) = surface
&& (self.architecture_surface() != surface
|| !self.canvas_target_visible(diagram, id))
{
if accepted_draft {
self.status =
"Properties applied. The view changed; select a visible target again."
.into();
}
return Task::none();
}
if let Some(d) = self
.active_diagram
.and_then(|d| self.project().diagrams.get(&d))
@@ -3432,7 +3576,7 @@ impl App {
self.workflow = Some(crate::workflow::Workflow::relationship_kind(
self.project(),
self.selected,
self.layer,
self.project().layer_of(self.selected).unwrap_or(self.layer),
kind,
incoming,
));
@@ -4458,13 +4602,18 @@ 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() == self.layer && e.kind().is_performer());
.is_some_and(|e| e.layer() == creation_layer && e.kind().is_performer());
let parent = if child && !allocated {
Some(self.selected)
} else {
@@ -4472,7 +4621,7 @@ impl App {
};
let Some(e) = self.project().make_element(
self.create_kind,
self.layer,
creation_layer,
&format!("New {}", self.create_kind.label().to_lowercase()),
parent,
) else {
@@ -4501,16 +4650,16 @@ impl App {
.project()
.diagrams
.values()
.find(|d| d.layer == self.layer && d.projection == preferred)
.find(|d| d.layer == creation_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}", self.layer),
name: format!("{} {preferred}", creation_layer),
description: String::new(),
layer: self.layer,
layer: creation_layer,
projection: preferred,
layout: None,
routes: Vec::new(),
@@ -4643,7 +4792,13 @@ 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();
@@ -4657,7 +4812,7 @@ impl App {
self.workflow = Some(crate::workflow::Workflow::relationship(
self.project(),
self.selected,
self.layer,
self.project().layer_of(self.selected).unwrap_or(self.layer),
));
}
fn add_trace(&mut self) {
@@ -5078,8 +5233,30 @@ impl App {
epoch: self.canvas_epoch,
})
}
// Admission belongs to the caller: palette choice, contextual source, or
// an already admitted session whose complete context is still current.
fn arm_connection(
&mut self,
context: crate::route_edit::Context,
tool: crate::connection_tool::Tool,
) -> crate::route_edit::Context {
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
let context = crate::route_edit::Context {
epoch: self.canvas_epoch,
..context
};
self.connection = Some(crate::connection_edit::Session::new(
self.project(),
&project_view(self.project(), context.diagram),
context,
tool,
));
self.selected_bend = None;
self.status = tool.hint().into();
context
}
fn edit_connection(&mut self, event: crate::connection_edit::Event) {
use crate::connection_edit::{Event, Session};
use crate::connection_edit::Event;
let Some(context) = self.route_context() else {
return;
};
@@ -5095,10 +5272,13 @@ impl App {
}
match event {
Event::Begin(tool, source) => {
self.edit_connection(Event::Choose(Some(tool)));
if let Some(context) = self.route_context() {
self.edit_connection(Event::Endpoint(context, source));
if !crate::connection_edit::can_begin(self.project(), context.diagram, tool, source)
{
self.status = "Choose a visible compatible source in this diagram.".into();
return;
}
let context = self.arm_connection(context, tool);
self.edit_connection(Event::Endpoint(context, source));
}
Event::Choose(Some(tool)) => {
if !crate::connection_tool::Tool::for_layer(self.layer).contains(&tool) {
@@ -5112,16 +5292,7 @@ impl App {
{
return;
}
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
let context = self.route_context().unwrap();
self.connection = Some(Session::new(
self.project(),
&project_view(self.project(), context.diagram),
context,
tool,
));
self.selected_bend = None;
self.status = tool.hint().into();
self.arm_connection(context, tool);
}
Event::Endpoint(captured, id) => {
if captured != context {
@@ -5182,17 +5353,9 @@ impl App {
&project_view(self.project(), context.diagram),
id,
);
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);
if !self.inspect_canvas_target(id) {
self.connection = Some(session);
return;
}
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()
@@ -5208,8 +5371,11 @@ impl App {
Event::Restart(captured) => {
if captured == context
&& let Some(session) = &self.connection
&& session.context == context
{
self.edit_connection(Event::Choose(Some(session.tool)));
// A contextual mixed-layer tool need not be in this layer's
// palette. Its unchanged session context retains admission.
self.arm_connection(context, session.tool);
}
}
Event::Choose(None) => {}
@@ -89,7 +89,10 @@ async fn refused_click_keeps_field(mounted: &mut Mounted, f: &mut Fixture, field
);
assert!(!messages.iter().any(|m| matches!(
m,
Message::Select(_) | Message::SelectNode(_) | Message::MoveNode(..)
Message::Select(_)
| Message::CanvasSelect(..)
| Message::SelectNode(_)
| Message::MoveNode(..)
)));
assert_eq!(f.app.document.bundle(), &before);
assert_eq!((f.app.selected, f.app.selected_node), selected);
@@ -237,7 +237,10 @@ fn mounted_dirty_property_press_does_not_select_apply_or_destroy_draft() {
));
assert!(!events.iter().any(|m| matches!(
m,
Message::Select(_) | Message::SelectNode(_) | Message::MoveNode(..)
Message::Select(_)
| Message::CanvasSelect(..)
| Message::SelectNode(_)
| Message::MoveNode(..)
)));
send(&mut f.app, events);
assert_eq!((f.app.selected, f.app.selected_node), selection);
@@ -1,5 +1,6 @@
//! 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::{
@@ -11,7 +12,7 @@ use iced::{
#[derive(Clone, Copy)]
enum Icon {
Element(Kind),
Arrow,
Connection(Tool),
Existing,
Select,
Collapse(bool),
@@ -76,10 +77,59 @@ impl canvas::Program<Message> for Icon {
);
}
}
Self::Arrow => {
f.stroke(&line(&[(1., 8.), (14., 8.), (10., 4.)]), stroke);
f.stroke(&line(&[(14., 8.), (10., 12.)]), stroke);
}
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::Existing => {
f.stroke(
&Path::rectangle(Point::new(1., 1.), Size::new(10., 10.)),
@@ -234,7 +284,7 @@ pub fn view(app: &App) -> Element<'_, Message> {
}
content = content.push(tool(
app,
Icon::Arrow,
Icon::Connection(*connection),
connection.label(),
Message::Connection(crate::connection_edit::Event::Choose(Some(*connection))),
app.connection
@@ -0,0 +1,904 @@
//! 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);
}
}
// Regression scope: a contextual click may accept a valid property draft, but
// must not inherit the draft object's semantic-layer navigation. If acceptance
// changes the originating diagram identity or hides the clicked target, retain
// that one accepted edit and refuse the old click. Do not infer a new target or
// reinterpret an old surface packet on a replacement diagram.
fn draft_click_fixture() -> (Fixture, Id) {
let mut f = fixture();
let mut project = f.app.project().clone();
let capability = project
.add_element(Kind::Capability, Layer::Oa, "Second capability", None)
.unwrap();
let other_edge = project.relate(
RelationKind::ChainInvolvement,
"Also enables",
f.process,
capability,
);
project.place(f.diagram, capability, Position { x: 500., y: 300. });
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();
assert!(
project_view(f.app.project(), f.diagram)
.edges
.iter()
.any(|edge| edge.id == other_edge)
);
(f, other_edge)
}
fn expected_draft_commit(app: &App, command: Command) -> Document {
let mut expected = Document::from_bundle(
app.document.bundle().clone(),
oa_core::source_project::ProjectLimits::default(),
)
.unwrap();
expected.execute(command).unwrap();
expected
}
fn assert_accepted_draft(app: &App, expected: &Document, revision: u64) {
assert_eq!(app.project(), expected.project());
assert_eq!(app.document.bundle(), expected.bundle());
assert_eq!(app.document.revision(), revision + 1);
assert!(!app.draft_dirty());
assert!(app.document.can_undo());
assert!(!app.document.can_redo());
assert_source_pair(app);
}
fn assert_one_draft_history(
app: &mut App,
before: &oa_core::source_project::CompiledSourceProject,
expected: &Document,
) {
let _ = app.update(Message::Undo);
assert_eq!(app.project(), &before.decoded.project);
assert_eq!(app.document.bundle(), &before.bundle);
assert!(
!app.document.can_undo(),
"the click must not add a second edit"
);
assert!(app.document.can_redo());
let _ = app.update(Message::Redo);
assert_eq!(app.project(), expected.project());
assert_eq!(app.document.bundle(), expected.bundle());
assert!(!app.document.can_redo());
}
fn pending_edge_name(f: &mut Fixture) {
let context = f.app.route_context().unwrap();
let _ = f.app.update(Message::CanvasSelect(context, f.edge));
assert_surface(f);
assert_eq!(f.app.selected, f.edge);
let _ = f
.app
.update(Message::Field("/name".into(), "Reviewed enables".into()));
assert!(f.app.draft_dirty());
assert!(f.app.draft.as_ref().unwrap().command().is_ok());
}
#[test]
fn valid_cross_layer_draft_then_canvas_edge_retains_surface_and_one_edit() {
let (mut f, other_edge) = draft_click_fixture();
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let expected =
expected_draft_commit(&f.app, Command::Rename(f.edge, "Reviewed enables".into()));
pending_edge_name(&mut f);
// Capture after drafting, so this exercises successful draft application,
// not refusal of a packet made stale by the property edit itself.
let context = f.app.route_context().unwrap();
let _ = f.app.update(Message::CanvasSelect(context, other_edge));
assert_accepted_draft(&f.app, &expected, revision);
assert_surface(&f);
assert_eq!(f.app.selected, other_edge);
assert_eq!(
f.app.multiselect_snapshot().unwrap().primary(),
Some(&Target::Edge(other_edge))
);
assert_one_draft_history(&mut f.app, &before, &expected);
}
#[test]
fn valid_cross_layer_draft_then_node_click_retains_surface_and_one_edit() {
let (mut f, _) = draft_click_fixture();
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let expected =
expected_draft_commit(&f.app, Command::Rename(f.edge, "Reviewed enables".into()));
pending_edge_name(&mut f);
let _ = f.app.update(Message::SelectNode(f.placement));
assert_accepted_draft(&f.app, &expected, revision);
assert_surface(&f);
assert_eq!(f.app.selected, f.process);
assert_eq!(f.app.selected_node, Some(f.placement));
assert_eq!(
f.app.multiselect_snapshot().unwrap().primary(),
Some(&Target::Placement(f.placement))
);
assert_one_draft_history(&mut f.app, &before, &expected);
}
#[test]
fn diagram_reidentity_draft_commits_but_refuses_the_old_surface_click() {
let (mut f, other_edge) = draft_click_fixture();
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let replacement = Id::new_v4();
let expected = expected_draft_commit(&f.app, Command::Reidentify(f.diagram, replacement));
let _ = f
.app
.update(Message::Field("/id".into(), replacement.to_string()));
assert!(f.app.draft_dirty());
let context = f.app.route_context().unwrap();
let _ = f.app.update(Message::CanvasSelect(context, other_edge));
assert_accepted_draft(&f.app, &expected, revision);
assert!(!f.app.project().diagrams.contains_key(&context.diagram));
assert!(
f.app
.active_diagram
.is_some_and(|id| f.app.project().diagrams.contains_key(&id)),
"never restore a removed diagram identity"
);
assert_ne!(f.app.selected, other_edge, "refuse the old surface click");
assert!(
f.app
.multiselect_snapshot()
.is_none_or(|s| !s.targets().contains(&Target::Edge(other_edge)))
);
assert_eq!(
f.app.status,
"Properties applied. The view changed; select a visible target again."
);
assert_one_draft_history(&mut f.app, &before, &expected);
}
#[test]
fn projection_draft_commits_but_refuses_the_now_hidden_canvas_target() {
let (mut f, other_edge) = draft_click_fixture();
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let mut diagram = f.app.project().diagrams[&f.diagram].clone();
diagram.projection = ProjectionKind::Allocation;
let expected = expected_draft_commit(&f.app, Command::EditDiagram(f.diagram, diagram));
let _ = f.app.update(Message::FieldShape(
"/projection".into(),
serde_json::to_value(ProjectionKind::Allocation).unwrap(),
));
assert!(f.app.draft_dirty());
let context = f.app.route_context().unwrap();
let _ = f.app.update(Message::CanvasSelect(context, other_edge));
assert_accepted_draft(&f.app, &expected, revision);
assert_surface(&f);
assert!(
!project_view(f.app.project(), f.diagram)
.edges
.iter()
.any(|edge| edge.id == other_edge)
);
assert_ne!(
f.app.selected, other_edge,
"do not inspect a no-longer-visible hit"
);
assert!(
f.app
.multiselect_snapshot()
.is_none_or(|s| !s.targets().contains(&Target::Edge(other_edge)))
);
assert_eq!(
f.app.status,
"Properties applied. The view changed; select a visible target again."
);
assert_one_draft_history(&mut f.app, &before, &expected);
}
#[test]
fn invalid_cross_layer_draft_blocks_contextual_clicks_without_changing_history() {
for click in 0..3 {
let (mut f, other_edge) = draft_click_fixture();
let context = f.app.route_context().unwrap();
let _ = f.app.update(Message::CanvasSelect(context, f.edge));
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let _ = f
.app
.update(Message::Field("/id".into(), "not-a-uuid".into()));
assert!(f.app.draft_dirty());
let expected_error = f.app.draft.as_ref().unwrap().command().unwrap_err();
let draft =
serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot()).unwrap();
let selection = f.app.multiselect_snapshot().unwrap();
let message = match click {
0 => Message::CanvasSelect(f.app.route_context().unwrap(), other_edge),
1 => Message::SelectNode(f.placement),
_ => Message::DiagramSelection(multiselect::Event::Choose(
f.app.multiselect_context().unwrap(),
Target::Edge(other_edge),
multiselect::Choice::Add,
)),
};
let _ = f.app.update(message);
if click < 2 {
assert_eq!(f.app.status, expected_error);
} else {
assert_eq!(
f.app.status,
"Finish or cancel the active edit before changing the diagram selection."
);
}
assert_surface(&f);
assert_eq!(f.app.selected, f.edge);
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());
assert!(!f.app.document.can_redo());
assert_eq!(
serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot()).unwrap(),
draft
);
assert_eq!(
f.app.multiselect_snapshot().unwrap().targets(),
selection.targets()
);
assert_eq!(
f.app.multiselect_snapshot().unwrap().primary(),
selection.primary()
);
if click == 0 {
// Pre-dispatch stale rejection applies no draft and must not emit
// either success feedback or a new validation message.
let stale = f.app.route_context().unwrap();
f.app.canvas_epoch += 1;
f.app.status = "Keep previous feedback".into();
let _ = f.app.update(Message::CanvasSelect(stale, other_edge));
assert_eq!(f.app.status, "Keep previous feedback");
assert_eq!(f.app.project(), &before.decoded.project);
assert_eq!(f.app.document.bundle(), &before.bundle);
assert_eq!(f.app.document.revision(), revision);
assert_eq!(
serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot()).unwrap(),
draft
);
}
}
}
#[test]
fn explicit_select_after_valid_cross_layer_draft_still_navigates_and_commits_once() {
let (mut f, other_edge) = draft_click_fixture();
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let expected =
expected_draft_commit(&f.app, Command::Rename(f.edge, "Reviewed enables".into()));
pending_edge_name(&mut f);
let _ = f.app.update(Message::Select(other_edge));
assert_accepted_draft(&f.app, &expected, revision);
assert_eq!(f.app.selected, other_edge);
assert_eq!(f.app.layer, Layer::Oa);
assert_ne!(f.app.active_diagram, Some(f.diagram));
assert_one_draft_history(&mut f.app, &before, &expected);
}
#[test]
fn properties_apply_on_cross_layer_edge_retains_surface_and_one_edit() {
let (mut f, _) = draft_click_fixture();
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let expected =
expected_draft_commit(&f.app, Command::Rename(f.edge, "Reviewed enables".into()));
pending_edge_name(&mut f);
let _ = f.app.update(Message::Apply);
assert_accepted_draft(&f.app, &expected, revision);
assert_eq!(f.app.status, "Properties applied");
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_one_draft_history(&mut f.app, &before, &expected);
}
#[test]
fn properties_apply_honors_an_intentional_active_diagram_layer_edit() {
let (mut f, _) = draft_click_fixture();
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let mut diagram = f.app.project().diagrams[&f.diagram].clone();
diagram.layer = Layer::Oa;
let expected = expected_draft_commit(&f.app, Command::EditDiagram(f.diagram, diagram));
let _ = f.app.update(Message::FieldShape(
"/layer".into(),
serde_json::to_value(Layer::Oa).unwrap(),
));
assert!(f.app.draft_dirty());
let _ = f.app.update(Message::Apply);
assert_accepted_draft(&f.app, &expected, revision);
assert_eq!(f.app.page, Page::Diagram);
assert_eq!(
f.app.layer,
Layer::Oa,
"honor the edited diagram's own layer"
);
assert_eq!(f.app.active_diagram, Some(f.diagram));
assert_eq!(f.app.selected, f.diagram);
assert_eq!(f.app.draft.as_ref().unwrap().original, f.diagram);
assert_one_draft_history(&mut f.app, &before, &expected);
}
#[test]
fn properties_apply_follows_the_known_active_diagram_reidentity_once() {
let (mut f, _) = draft_click_fixture();
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let replacement = Id::new_v4();
let expected = expected_draft_commit(&f.app, Command::Reidentify(f.diagram, replacement));
let _ = f
.app
.update(Message::Field("/id".into(), replacement.to_string()));
assert!(f.app.draft_dirty());
let _ = f.app.update(Message::Apply);
assert_accepted_draft(&f.app, &expected, revision);
assert_eq!(f.app.page, Page::Diagram);
assert_eq!(f.app.layer, Layer::Sa);
// This identity was explicitly authored in the diagram's own draft. No
// queued click is being remapped or inferred from semantic similarity.
assert_eq!(f.app.active_diagram, Some(replacement));
assert_eq!(f.app.selected, replacement);
assert_eq!(f.app.draft.as_ref().unwrap().original, replacement);
assert!(!f.app.project().diagrams.contains_key(&f.diagram));
assert_one_draft_history(&mut f.app, &before, &expected);
}
@@ -0,0 +1,366 @@
// Test-only proposal pinned to peer 6520a89dc042dd433454f6cc63cb23b0e6df7911.
// Independent App test module; real popup messages retain menu tokens.
use super::*;
use crate::model_menu::{Event as MenuEvent, Request};
struct BeginFixture {
app: App,
diagram: Id,
surface_layer: Layer,
source: Id,
target: Id,
wrong_layer: Id,
hidden: Id,
tool: crate::connection_tool::Tool,
}
fn begin_fixture(source_layer: Layer, allocation: bool, mixed: bool) -> BeginFixture {
use crate::connection_tool::Tool;
let mut p = Project::empty();
let (work, performer, exchange) = match source_layer {
Layer::Oa => (Kind::Activity, Kind::Entity, Tool::Interaction),
Layer::Sa => (Kind::Function, Kind::System, Tool::FunctionalExchange),
};
let opposite = if source_layer == Layer::Oa {
Layer::Sa
} else {
Layer::Oa
};
let surface_layer = if mixed { opposite } else { source_layer };
let source = p
.add_element(
if allocation { performer } else { work },
source_layer,
"Source",
None,
)
.unwrap();
let target = p.add_element(work, source_layer, "Target", None).unwrap();
let hidden = p
.add_element(work, source_layer, "Hidden target", None)
.unwrap();
let other_work = if opposite == Layer::Oa {
Kind::Activity
} else {
Kind::Function
};
let wrong_layer = p
.add_element(other_work, opposite, "Wrong layer target", None)
.unwrap();
p.ensure_diagram(source_layer);
let diagram = p.ensure_diagram(surface_layer);
p.diagrams.get_mut(&diagram).unwrap().projection = if mixed {
ProjectionKind::Traceability
} else {
ProjectionKind::Capabilities
};
p.place(diagram, source, Position { x: 0., y: 0. });
p.place(diagram, target, Position { x: 400., y: 0. });
if mixed {
p.place(diagram, wrong_layer, Position { x: 800., y: 0. });
}
let mut app = App::with_project(p);
app.page = Page::Diagram;
app.layer = surface_layer;
app.active_diagram = Some(diagram);
app.selected = diagram;
app.refresh_draft();
app.reconcile_multiselect();
assert!(!app.document.can_undo());
BeginFixture {
app,
diagram,
surface_layer,
source,
target,
wrong_layer,
hidden,
tool: if allocation {
Tool::Allocation
} else {
exchange
},
}
}
fn begin_surface(f: &BeginFixture) {
assert_eq!(f.app.page, Page::Diagram);
assert_eq!(f.app.layer, f.surface_layer);
assert_eq!(f.app.active_diagram, Some(f.diagram));
}
fn begin_popup_action(f: &mut BeginFixture) -> Option<Message> {
begin_popup_action_from(f, true)
}
fn begin_popup_action_from(f: &mut BeginFixture, canvas: bool) -> Option<Message> {
let mut request = Request::object(f.source, iced::Point::ORIGIN);
if canvas {
request.canvas = f.app.route_context();
request.placement = f.app.project().diagrams[&f.diagram]
.nodes
.values()
.find(|node| node.element == f.source)
.map(|node| node.id);
}
let _ = f.app.update(Message::Menu(MenuEvent::Open(request)));
let popup = crate::model_menu::popup(&f.app).expect("real model popup");
let expected = format!("Draw {}", f.tool.label().to_lowercase());
for entry in popup.entries {
if let crate::context_menu::Entry::Action { label, message, .. } = entry
&& label == expected
{
if let Some(Message::Menu(MenuEvent::Invoke(token, _))) = &message {
assert_eq!(*token, popup.token);
} else {
assert!(message.is_none(), "Draw must use the menu token envelope");
}
return message;
}
}
None // Omission is also a valid unavailable-action policy.
}
fn begin_from_popup(f: &mut BeginFixture) -> Message {
begin_from_popup_origin(f, true)
}
fn begin_from_popup_origin(f: &mut BeginFixture, canvas: bool) -> Message {
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let action =
begin_popup_action_from(f, canvas).expect("visible semantic source offers enabled Draw");
let _ = f.app.update(action.clone());
begin_surface(f);
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());
let session = f
.app
.connection
.as_ref()
.expect("menu Draw must arm its tool");
assert_eq!(session.tool, f.tool);
assert_eq!(session.source, Some(f.source));
assert_eq!(session.context, f.app.route_context().unwrap());
assert!(session.candidates.contains(&f.target));
assert!(!session.candidates.contains(&f.wrong_layer));
assert!(!session.candidates.contains(&f.hidden));
action
}
fn begin_complete_and_check_history(f: &mut BeginFixture) {
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let context = f.app.route_context().unwrap();
let _ = f.app.update(Message::Connection(
crate::connection_edit::Event::Endpoint(context, f.target),
));
begin_surface(f);
assert!(f.app.connection.is_none());
assert_eq!(f.app.document.revision(), revision + 1);
let relations: Vec<_> = f
.app
.project()
.relations
.values()
.filter(|r| !before.decoded.project.relations.contains_key(&r.id))
.collect();
assert_eq!(relations.len(), 1);
let expected = match f.tool {
crate::connection_tool::Tool::Interaction => RelationKind::Interaction,
crate::connection_tool::Tool::FunctionalExchange => RelationKind::FunctionalExchange,
crate::connection_tool::Tool::Allocation => RelationKind::Allocation,
_ => unreachable!(),
};
assert_eq!(relations[0].kind(), expected);
assert_eq!(
(relations[0].source(), relations[0].target()),
(f.source, f.target)
);
// Random identities are accepted only for the precise command delta.
// Everything else, including traces, metadata and view options, stays exact.
let mut expected_project = before.decoded.project.clone();
let created = relations[0];
assert!(
expected_project
.relations
.insert(created.id, created.clone())
.is_none()
);
if let RelationBody::FunctionalExchange {
source_port,
target_port,
items,
..
} = &created.body
{
assert_eq!(f.tool, crate::connection_tool::Tool::FunctionalExchange);
assert!(items.is_empty());
assert_ne!(source_port, target_port);
let output = &f.app.project().elements[source_port];
let input = &f.app.project().elements[target_port];
assert_eq!(
(output.kind(), output.layer(), output.parent),
(Kind::Port, Layer::Sa, Some(f.source))
);
assert_eq!(
(input.kind(), input.layer(), input.parent),
(Kind::Port, Layer::Sa, Some(f.target))
);
assert!(
matches!(port_contract(output), Some(PortContract::FunctionOutput { outgoing_items }) if outgoing_items.is_empty())
);
assert!(
matches!(port_contract(input), Some(PortContract::FunctionInput { incoming_items }) if incoming_items.is_empty())
);
assert!(!before.decoded.project.elements.contains_key(source_port));
assert!(!before.decoded.project.elements.contains_key(target_port));
assert_eq!(output.id, *source_port);
assert_eq!(input.id, *target_port);
expected_project
.elements
.insert(*source_port, output.clone());
expected_project
.elements
.insert(*target_port, input.clone());
assert_eq!(
f.app.project().elements.len(),
before.decoded.project.elements.len() + 2
);
} else {
assert_ne!(f.tool, crate::connection_tool::Tool::FunctionalExchange);
assert_eq!(f.app.project().elements, before.decoded.project.elements);
}
assert_eq!(f.app.project(), &expected_project);
assert_eq!(f.app.project().diagrams, before.decoded.project.diagrams);
for (id, element) in &before.decoded.project.elements {
assert_eq!(&f.app.project().elements[id], element);
}
begin_assert_source_pair(&f.app);
let after = 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(),
"creation is a single history step"
);
let _ = f.app.update(Message::Redo);
assert_eq!(f.app.project(), &after.decoded.project);
assert_eq!(f.app.document.bundle(), &after.bundle);
begin_assert_source_pair(&f.app);
}
#[test]
fn contextual_begin_exchange_uses_actual_menu_in_both_mixed_directions() {
for layer in [Layer::Oa, Layer::Sa] {
let mut f = begin_fixture(layer, false, true);
begin_from_popup(&mut f);
begin_complete_and_check_history(&mut f);
}
}
#[test]
fn contextual_begin_cancel_restart_and_stale_menu_preserve_session_ownership() {
for layer in [Layer::Oa, Layer::Sa] {
let mut f = begin_fixture(layer, false, true);
let original = f.app.document.snapshot();
let stale_menu = begin_from_popup(&mut f);
let first = f.app.route_context().unwrap();
let _ = f
.app
.update(Message::Connection(crate::connection_edit::Event::Restart(
first,
)));
begin_surface(&f);
let restarted = f
.app
.connection
.as_ref()
.expect("restart retains contextual tool");
assert_eq!(restarted.tool, f.tool);
assert_eq!(restarted.source, None);
assert!(restarted.candidates.contains(&f.source));
let current = restarted.context;
assert_ne!(current, first);
let _ = f.app.update(stale_menu);
let _ = f.app.update(Message::Connection(
crate::connection_edit::Event::Endpoint(first, f.target),
));
assert_eq!(f.app.connection.as_ref().unwrap().context, current);
assert_eq!(f.app.connection.as_ref().unwrap().source, None);
let _ = f.app.update(Message::Connection(
crate::connection_edit::Event::Endpoint(current, f.source),
));
assert_eq!(f.app.connection.as_ref().unwrap().source, Some(f.source));
let _ = f.app.update(Message::Escape);
let _ = f.app.update(Message::Connection(
crate::connection_edit::Event::Endpoint(current, f.target),
));
assert!(f.app.connection.is_none());
begin_surface(&f);
assert_eq!(f.app.project(), &original.decoded.project);
assert_eq!(f.app.document.bundle(), &original.bundle);
assert!(!f.app.document.can_undo());
}
}
#[test]
fn contextual_begin_allocation_uses_actual_menu_in_both_mixed_directions() {
for layer in [Layer::Oa, Layer::Sa] {
let mut f = begin_fixture(layer, true, true);
begin_from_popup(&mut f);
begin_complete_and_check_history(&mut f);
}
}
#[test]
fn contextual_begin_allocation_is_not_enabled_when_projection_hides_work() {
for layer in [Layer::Oa, Layer::Sa] {
let mut f = begin_fixture(layer, true, false);
let view = project_view(f.app.project(), f.diagram);
assert!(view.nodes.iter().any(|n| n.element == f.source));
assert!(!view.nodes.iter().any(|n| n.element == f.target));
assert!(!f.tool.available_in(layer, ProjectionKind::Capabilities));
let before = f.app.document.snapshot();
assert!(
begin_popup_action(&mut f).is_none(),
"Draw allocation must be disabled or omitted when work is not projected"
);
let _ = f.app.update(Message::Menu(MenuEvent::Close));
// Defensive handler refusal complements actual-menu enabledness above.
let _ = f
.app
.update(Message::Connection(crate::connection_edit::Event::Begin(
f.tool, f.source,
)));
assert!(f.app.connection.is_none());
begin_surface(&f);
assert_eq!(f.app.project(), &before.decoded.project);
assert_eq!(f.app.document.bundle(), &before.bundle);
assert!(!f.app.document.can_undo());
}
}
fn begin_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 contextual_begin_explorer_draw_retains_visible_mixed_diagram_in_both_directions() {
for layer in [Layer::Oa, Layer::Sa] {
let mut f = begin_fixture(layer, false, true);
// A real Explorer Request::object has no canvas/placement envelope,
// but its generated Draw action still targets the visible current view.
begin_from_popup_origin(&mut f, false);
begin_complete_and_check_history(&mut f);
}
}
@@ -721,3 +721,538 @@ async fn context_help_barrier_retires_text_selection_drag_without_changing_the_c
"An unheld pointer no longer extends the pre-Help text selection"
);
}
// Union coverage: Help must own input before mixed-canvas draft/Draw admission.
struct MixedHelp {
app: App,
diagram: Id,
source: Id,
target: Id,
edge: Id,
placement: Id,
tool: crate::connection_tool::Tool,
}
fn mixed_help(layer: Layer) -> MixedHelp {
use crate::connection_tool::Tool;
let mut p = Project::empty();
let (kind, other, tool) = match layer {
Layer::Oa => (Kind::Activity, Layer::Sa, Tool::Interaction),
Layer::Sa => (Kind::Function, Layer::Oa, Tool::FunctionalExchange),
};
let source = p.add_element(kind, layer, "Send", None).unwrap();
let target = p.add_element(kind, layer, "Receive", None).unwrap();
p.ensure_diagram(layer);
let diagram = p.ensure_diagram(other);
p.diagrams.get_mut(&diagram).unwrap().projection = ProjectionKind::Traceability;
let placement = p.place(diagram, source, Position { x: 20., y: 20. });
p.place(diagram, target, Position { x: 400., y: 20. });
let local_kind = if other == Layer::Oa {
Kind::Activity
} else {
Kind::Function
};
let local = p
.add_element(local_kind, other, "Other layer work", None)
.unwrap();
p.place(diagram, local, Position { x: 400., y: 240. });
let edge_kind = if layer == Layer::Oa {
RelationKind::Interaction
} else {
RelationKind::FunctionalExchange
};
let edge = p.relate(edge_kind, "Existing transfer", source, target);
let mut app = App::with_project(p);
app.page = Page::Diagram;
app.layer = other;
app.active_diagram = Some(diagram);
app.selected = diagram;
app.refresh_draft();
app.reconcile_multiselect();
choose(&mut app, placement, multiselect::Choice::Replace);
assert_eq!(app.selected, source);
assert_eq!((app.layer, app.active_diagram), (other, Some(diagram)));
assert_ne!(app.selected, edge);
assert!(
project_view(app.project(), diagram)
.edges
.iter()
.any(|visible| visible.id == edge)
);
MixedHelp {
app,
diagram,
source,
target,
edge,
placement,
tool,
}
}
fn mixed_help_popup(f: &mut MixedHelp) -> Message {
use crate::model_menu::{Event, Request};
let mut request = Request::object(f.source, iced::Point::ORIGIN);
request.canvas = f.app.route_context();
request.placement = Some(f.placement);
send(&mut f.app, Message::Menu(Event::Open(request)));
let popup = crate::model_menu::popup(&f.app).expect("actual mixed-canvas popup");
let label = format!("Draw {}", f.tool.label().to_lowercase());
let action = popup
.entries
.into_iter()
.find_map(|entry| match entry {
crate::context_menu::Entry::Action {
label: actual,
message,
..
} if actual == label => message,
_ => None,
})
.expect("visible opposite-layer source offers Draw");
assert!(matches!(&action, Message::Menu(Event::Invoke(token, _)) if *token == popup.token));
action
}
fn mixed_help_workspace(app: &App) -> impl PartialEq + std::fmt::Debug + use<> {
(
(
app.page,
app.layer,
app.active_diagram,
app.selected,
app.selected_node,
),
(app.zoom, app.pan),
app.multiselect_snapshot().map(|s| s.targets().to_vec()),
serde_json::to_value(app.draft.as_ref().unwrap().recovery_snapshot()).unwrap(),
(app.source_editor.text(), app.source_version),
(
app.busy,
app.file_prompt.is_some(),
app.pending.is_some(),
app.workflow.is_some(),
),
app.model_menu.as_ref().map(|m| m.token),
)
}
fn mixed_help_state(app: &App) -> impl PartialEq + std::fmt::Debug + use<> {
(
mixed_help_workspace(app),
app.connection
.as_ref()
.map(|s| (s.tool, s.source, s.context, s.candidates.clone())),
)
}
fn mixed_help_document(
app: &App,
before: &oa_core::source_project::CompiledSourceProject,
revision: u64,
) {
assert_eq!(app.project(), &before.decoded.project);
assert_eq!(app.document.bundle(), &before.bundle);
assert_eq!(app.document.revision(), revision);
assert!(!app.document.can_undo());
assert!(!app.document.can_redo());
}
#[test]
fn mixed_context_help_blocks_navigation_draft_draw_and_save_before_fast_paths() {
use crate::connection_edit::Event as Draw;
use crate::model_menu::{Event as Menu, Request};
for layer in [Layer::Oa, Layer::Sa] {
for draft in 0..3 {
let mut f = mixed_help(layer);
let queued_popup = mixed_help_popup(&mut f);
if draft == 0 {
send(&mut f.app, queued_popup.clone());
let session = f
.app
.connection
.as_ref()
.expect("clean row has an admitted Draw session");
assert_eq!((session.tool, session.source), (f.tool, Some(f.source)));
assert!(session.candidates.contains(&f.target));
} else {
send(&mut f.app, Message::Menu(Menu::Close));
}
match draft {
1 => send(
&mut f.app,
Message::Field("/name".into(), "Pending mixed name".into()),
),
2 => send(
&mut f.app,
Message::Field("/alias".into(), "invalid alias".into()),
),
_ => {}
}
assert_eq!(f.app.draft_dirty(), draft != 0);
assert_eq!(f.app.connection.is_some(), draft == 0);
let command = f.app.draft.as_ref().unwrap().command().unwrap();
assert_eq!(
oa_core::prepare_command(f.app.project(), &command).is_err(),
draft == 2
);
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let original = mixed_help_state(&f.app);
send(&mut f.app, Message::Help);
assert!(f.app.context_help);
assert_eq!(mixed_help_state(&f.app), original);
let current = f.app.route_context().unwrap();
let mut stale = current;
stale.epoch = stale.epoch.wrapping_add(1);
let mut request = Request::object(f.target, iced::Point::ORIGIN);
request.canvas = Some(current);
for message in [
Message::CanvasSelect(current, f.edge),
Message::CanvasSelect(stale, f.edge),
Message::Menu(Menu::Open(request)),
queued_popup,
Message::Connection(Draw::Endpoint(current, f.target)),
Message::Connection(Draw::Endpoint(stale, f.target)),
Message::Connection(Draw::Restart(current)),
Message::Connection(Draw::Restart(stale)),
Message::Save(false),
Message::Save(true),
Message::Apply,
Message::Undo,
] {
assert!(
iced_runtime::task::into_stream(f.app.update(message)).is_none(),
"blocked input must not schedule I/O or widget work"
);
assert!(f.app.context_help);
assert_eq!(mixed_help_state(&f.app), original);
mixed_help_document(&f.app, &before, revision);
}
send(&mut f.app, Message::CloseContextHelp);
assert!(!f.app.context_help);
assert_eq!(
mixed_help_state(&f.app),
original,
"closing cannot replay queued input"
);
mixed_help_document(&f.app, &before, revision);
}
}
}
async fn mixed_help_click_close(
mounted: &mut crate::behavior::canvas_workspace::tests::Mounted,
app: &mut App,
) {
use iced::{advanced::widget, mouse};
// Observe real header containers and require the actual button's message.
// No direct Close dispatch masquerades as pointer/focus coverage.
#[derive(Default)]
struct Bounds(Vec<iced::Rectangle>);
impl widget::Operation for Bounds {
fn traverse(&mut self, visit: &mut dyn FnMut(&mut dyn widget::Operation)) {
visit(self);
}
fn container(&mut self, _: Option<&widget::Id>, bounds: iced::Rectangle) {
self.0.push(bounds);
}
}
let article =
mounted.geometry(app).scrolls[&widget::Id::new(crate::guide::ARTICLE_SCROLL_ID)].0;
let mut bounds = Bounds::default();
mounted.operate(app, &mut bounds);
for rect in bounds.0.into_iter().filter(|r| {
r.width < 180.
&& r.height < 60.
&& r.x > article.x + article.width - 180.
&& r.y < article.y
&& r.y > article.y - 80.
}) {
let at = rect.center();
let messages = mounted.input(
app,
&[
iced::Event::Mouse(mouse::Event::CursorMoved { position: at }),
iced::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)),
iced::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)),
],
mouse::Cursor::Available(at),
);
assert!(
messages
.iter()
.all(|m| matches!(m, Message::CloseContextHelp)),
"a candidate Close click must not leak another workspace command: {messages:?}"
);
if !messages.is_empty() {
assert_eq!(
messages.len(),
1,
"one pointer click emits exactly one Close"
);
let task = app.update(messages.into_iter().next().unwrap());
mounted.perform(app, task).await;
return;
}
}
panic!("mounted Help Close button did not emit CloseContextHelp");
}
#[tokio::test]
async fn mounted_mixed_context_help_preserves_name_and_invalid_alias_carets_then_applies_once() {
use crate::behavior::canvas_workspace::tests::{Mounted, character, key};
use iced::keyboard::key::Named;
for layer in [Layer::Oa, Layer::Sa] {
for invalid in [false, true] {
for escape in [false, true] {
let mut f = mixed_help(layer);
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let mut mounted = Mounted::new(&mut f.app).await;
let mut inputs: Vec<_> = mounted
.geometry(&f.app)
.text_inputs
.into_iter()
.filter(|b| b.x > f.app.window_size.width - 320.)
.collect();
inputs.sort_by(|a, b| a.y.total_cmp(&b.y));
// Production forms render Name then Alias; assert the resulting raw field.
let path = if invalid { "/alias" } else { "/name" };
let old = f.app.draft.as_ref().unwrap().inputs[path].raw.clone();
mounted
.click(&mut f.app, inputs[usize::from(invalid)].center())
.await;
help_key(&mut mounted, &mut f.app, key(Named::Home)).await;
let prefix = if invalid { "bad alias " } else { "DRAFT " };
for value in prefix.chars() {
help_key(&mut mounted, &mut f.app, character(&value.to_string())).await;
}
help_key(&mut mounted, &mut f.app, key(Named::ArrowLeft)).await;
let raw = format!("{prefix}{old}");
assert_eq!(f.app.draft.as_ref().unwrap().inputs[path].raw, raw);
let command = f.app.draft.as_ref().unwrap().command().unwrap();
assert_eq!(
oa_core::prepare_command(f.app.project(), &command).is_err(),
invalid
);
let state = mixed_help_state(&f.app);
help_key(&mut mounted, &mut f.app, key(Named::F1)).await;
assert!(f.app.context_help);
let article = iced::advanced::widget::Id::new(crate::guide::ARTICLE_SCROLL_ID);
help_key(&mut mounted, &mut f.app, key(Named::PageDown)).await;
assert!(mounted.geometry(&f.app).scrolls[&article].1.y > 0.);
help_key(&mut mounted, &mut f.app, key(Named::End)).await;
help_key(&mut mounted, &mut f.app, character("IGNORED")).await;
let messages = mounted
.batch(
&mut f.app,
&[iced::Event::InputMethod(
iced::advanced::input_method::Event::Commit("雨".into()),
)],
iced::mouse::Cursor::Unavailable,
)
.await;
assert!(!messages.iter().any(|m| matches!(m, Message::Field(..))));
assert_eq!(mixed_help_state(&f.app), state);
mixed_help_document(&f.app, &before, revision);
if escape {
help_key(&mut mounted, &mut f.app, key(Named::Escape)).await;
} else {
mixed_help_click_close(&mut mounted, &mut f.app).await;
}
assert!(!f.app.context_help);
assert_eq!(mixed_help_state(&f.app), state);
help_key(&mut mounted, &mut f.app, character("X")).await;
let expected_raw = format!("{}X {old}", prefix.trim_end());
assert_eq!(
f.app.draft.as_ref().unwrap().inputs[path].raw,
expected_raw,
"exact mid-string caret survives without refocusing"
);
let surface = (
f.app.page,
f.app.layer,
f.app.active_diagram,
f.app.selected,
f.app.zoom,
f.app.pan,
);
if invalid {
let recovery =
serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot())
.unwrap();
assert!(!f.app.apply());
assert_eq!(
serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot())
.unwrap(),
recovery
);
mixed_help_document(&f.app, &before, revision);
assert_eq!(
(
f.app.page,
f.app.layer,
f.app.active_diagram,
f.app.selected,
f.app.zoom,
f.app.pan
),
surface
);
} else {
let mut expected = Document::from_bundle(
before.bundle.clone(),
oa_core::source_project::ProjectLimits::default(),
)
.unwrap();
expected
.execute(Command::Rename(f.source, expected_raw.clone()))
.unwrap();
let mut expected_project = before.decoded.project.clone();
expected_project.elements.get_mut(&f.source).unwrap().name = expected_raw;
assert!(f.app.apply());
assert_eq!(f.app.project(), &expected_project);
assert_eq!(f.app.document.bundle(), expected.bundle());
assert_eq!(f.app.document.revision(), revision + 1);
assert!(!f.app.document.can_redo());
assert_eq!(
(
f.app.page,
f.app.layer,
f.app.active_diagram,
f.app.selected,
f.app.zoom,
f.app.pan
),
surface
);
send(&mut f.app, Message::Undo);
assert_eq!(f.app.project(), &before.decoded.project);
assert_eq!(f.app.document.bundle(), &before.bundle);
assert!(!f.app.document.can_undo());
assert!(f.app.document.can_redo());
send(&mut f.app, Message::Redo);
assert_eq!(f.app.project(), &expected_project);
assert_eq!(f.app.document.bundle(), expected.bundle());
assert!(!f.app.document.can_redo());
}
}
}
}
}
#[tokio::test]
async fn mounted_mixed_context_help_preserves_draw_session_but_retires_held_release() {
use crate::behavior::canvas_workspace::tests::{Mounted, key};
use crate::connection_edit::Event as Draw;
use iced::{keyboard::key::Named, mouse};
for layer in [Layer::Oa, Layer::Sa] {
let mut f = mixed_help(layer);
f.app.window_size = iced::Size::new(2000., 1400.);
f.app.zoom = 1.;
f.app.pan = iced::Vector::ZERO;
f.app.fit_to_view = false;
let mut mounted = Mounted::new(&mut f.app).await;
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let popup = mixed_help_popup(&mut f);
send(&mut f.app, popup);
let session = f.app.connection.as_ref().expect("actual popup armed Draw");
assert_eq!((session.tool, session.source), (f.tool, Some(f.source)));
assert!(session.candidates.contains(&f.target));
let context = session.context;
send(&mut f.app, Message::Connection(Draw::Restart(context)));
let session = f.app.connection.as_ref().unwrap();
assert_eq!((session.tool, session.source), (f.tool, None));
let context = session.context;
let bounds = mounted
.geometry(&f.app)
.bounds(crate::allocation_input::CANVAS_ID);
let view = project_view(f.app.project(), f.diagram);
let point = |id| {
let node = view.nodes.iter().find(|node| node.element == id).unwrap();
let point = iced::Point::new(
bounds.x + (node.position.x + node.size.width / 2.) * f.app.zoom + f.app.pan.x,
bounds.y + (node.position.y + node.size.height / 2.) * f.app.zoom + f.app.pan.y,
);
assert!(bounds.contains(point));
point
};
let source = point(f.source);
let target = point(f.target);
let messages = mounted
.batch(
&mut f.app,
&[
iced::Event::Mouse(mouse::Event::CursorMoved { position: source }),
iced::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)),
],
mouse::Cursor::Available(source),
)
.await;
assert!(messages.iter().any(|m| matches!(m, Message::Connection(Draw::Endpoint(c, id)) if *c == context && *id == f.source)));
let session = f.app.connection.as_ref().unwrap();
assert_eq!((session.tool, session.source), (f.tool, Some(f.source)));
assert!(session.candidates.contains(&f.target));
let context = session.context;
// The admitted multi-click tool/source is reading state. Only the held
// widget gesture is retired; a later deliberate click is still valid.
let state = mixed_help_state(&f.app);
mixed_help_document(&f.app, &before, revision);
help_key(&mut mounted, &mut f.app, key(Named::F1)).await;
assert!(f.app.context_help);
assert_eq!(mixed_help_state(&f.app), state);
for message in [
Message::Connection(Draw::Endpoint(context, f.target)),
Message::Connection(Draw::Restart(context)),
] {
assert!(iced_runtime::task::into_stream(f.app.update(message)).is_none());
assert_eq!(mixed_help_state(&f.app), state);
mixed_help_document(&f.app, &before, revision);
}
let release = [
iced::Event::Mouse(mouse::Event::CursorMoved { position: target }),
iced::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)),
];
let messages = mounted
.batch(&mut f.app, &release, mouse::Cursor::Available(target))
.await;
assert!(!messages.iter().any(|m| matches!(m, Message::Connection(_))));
assert_eq!(mixed_help_state(&f.app), state);
mixed_help_document(&f.app, &before, revision);
help_key(&mut mounted, &mut f.app, key(Named::Escape)).await;
assert!(!f.app.context_help);
let messages = mounted
.batch(&mut f.app, &release, mouse::Cursor::Available(target))
.await;
assert!(
!messages.iter().any(|m| matches!(m, Message::Connection(_))),
"a release after closing Help cannot resume the held press"
);
assert_eq!(mixed_help_state(&f.app), state);
mixed_help_document(&f.app, &before, revision);
// Observe the real fresh-click message without executing a randomly
// identified creation; existing connection tests own its semantic delta.
let current = f.app.route_context().unwrap();
let messages = mounted.input(
&f.app,
&[
iced::Event::Mouse(mouse::Event::CursorMoved { position: target }),
iced::Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)),
iced::Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)),
],
mouse::Cursor::Available(target),
);
let connections: Vec<_> = messages
.iter()
.filter(|m| matches!(m, Message::Connection(_)))
.collect();
assert_eq!(connections.len(), 1);
assert!(
matches!(connections[0], Message::Connection(Draw::Endpoint(c, id)) if *c == current && *id == f.target)
);
assert_eq!(mixed_help_state(&f.app), state);
mixed_help_document(&f.app, &before, revision);
}
}
@@ -0,0 +1,339 @@
// Proposed tests against c7065db76ecca3faa733ccb5f09b87fd0a0958be.
use super::*;
struct RelocationFixture {
app: App,
source: Id,
target: Id,
node: Id,
other_node: Id,
edge: Id,
}
fn relocation_fixture(source_layer: Layer, target_layer: Layer) -> RelocationFixture {
let mut p = Project::empty();
let work = p
.add_element(Kind::Activity, Layer::Oa, "Observed work", None)
.unwrap();
let other = p
.add_element(Kind::Activity, Layer::Oa, "Other work", None)
.unwrap();
let edge = p.relate(RelationKind::Interaction, "Observed exchange", work, other);
let source = p.ensure_diagram(source_layer);
p.diagrams.get_mut(&source).unwrap().projection = ProjectionKind::Traceability;
let node = p.place(source, work, Position { x: 20., y: 20. });
let other_node = p.place(source, other, Position { x: 500., y: 20. });
let mut destination = p.diagrams[&source].clone();
destination.id = Id::new_v4();
destination.alias = "relocation_destination".into();
destination.name = "Relocation destination".into();
destination.layer = target_layer;
destination.nodes.clear();
let target = destination.id;
p.diagrams.insert(target, destination);
let mut app = App::with_project(p);
app.page = Page::Diagram;
app.layer = source_layer;
app.active_diagram = Some(source);
app.selected = node;
app.refresh_draft();
app.reconcile_multiselect();
assert!(
matches!(app.draft.as_ref().unwrap().kind, crate::forms::RecordKind::Node(d) if d == source)
);
assert!(!app.document.can_undo());
RelocationFixture {
app,
source,
target,
node,
other_node,
edge,
}
}
fn relocation_field(app: &mut App, path: &str, value: impl ToString) {
let _ = app.update(Message::Field(path.into(), value.to_string()));
}
fn relocation_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());
}
fn relocation_selection(app: &App, diagram: Id, placement: Id) {
assert_eq!(app.page, Page::Diagram);
assert_eq!(app.active_diagram, Some(diagram));
assert_eq!(app.layer, app.project().diagrams[&diagram].layer);
assert_eq!(app.selected, placement);
assert_eq!(app.selected_node, Some(placement));
let draft = app.draft.as_ref().unwrap();
assert_eq!(draft.original, placement);
assert!(matches!(draft.kind, crate::forms::RecordKind::Node(d) if d == diagram));
}
fn relocation_expected(app: &App, command: Command) -> Document {
let mut expected = Document::from_bundle(
app.document.bundle().clone(),
oa_core::source_project::ProjectLimits::default(),
)
.unwrap();
expected.execute(command).unwrap();
expected
}
fn relocation_history(
app: &mut App,
before: &oa_core::source_project::CompiledSourceProject,
expected: &Document,
) {
assert_eq!(app.project(), expected.project());
assert_eq!(app.document.bundle(), expected.bundle());
assert!(app.document.can_undo());
assert!(!app.document.can_redo());
let _ = app.update(Message::Undo);
assert_eq!(app.project(), &before.decoded.project);
assert_eq!(app.document.bundle(), &before.bundle);
assert!(!app.document.can_undo());
assert!(app.document.can_redo());
let _ = app.update(Message::Redo);
assert_eq!(app.project(), expected.project());
assert_eq!(app.document.bundle(), expected.bundle());
assert!(!app.document.can_redo());
relocation_source_pair(app);
}
#[test]
fn explicit_placement_relocation_follows_actual_owner_with_optional_new_identity() {
for layer in [Layer::Oa, Layer::Sa] {
for reidentify in [false, true] {
let mut f = relocation_fixture(Layer::Oa, layer);
let before = f.app.document.snapshot();
let before_project = before.decoded.project.clone();
let revision = f.app.document.revision();
let id = if reidentify { Id::new_v4() } else { f.node };
let mut expected = before_project.clone();
let mut placement = expected
.diagrams
.get_mut(&f.source)
.unwrap()
.nodes
.remove(&f.node)
.unwrap();
placement.id = id;
expected
.diagrams
.get_mut(&f.target)
.unwrap()
.nodes
.insert(id, placement);
let expected_document = relocation_expected(
&f.app,
Command::Batch(vec![
Command::EditNode {
diagram: f.source,
original: f.node,
node: expected.diagrams[&f.target].nodes[&id].clone(),
},
Command::RelocateNode {
node: id,
diagram: f.target,
},
]),
);
assert_eq!(expected_document.project(), &expected);
relocation_field(&mut f.app, "/diagram", f.target);
if reidentify {
relocation_field(&mut f.app, "/id", id);
}
assert!(f.app.apply(), "{}", f.app.status);
assert_eq!(f.app.project(), &expected);
assert_eq!(f.app.document.revision(), revision + 1);
relocation_selection(&f.app, f.target, id);
relocation_source_pair(&f.app);
assert_eq!(f.app.document.bundle(), expected_document.bundle());
relocation_history(&mut f.app, &before, &expected_document);
}
}
}
#[test]
fn same_owner_placement_position_alias_and_identity_keep_mixed_surface() {
for different_current_view in [false, true] {
for field in ["/position/x", "/alias", "/id"] {
let mut f = relocation_fixture(Layer::Sa, Layer::Oa);
// Ordinary Select plus the diagram picker can retain an independent
// placement Inspector while showing a different saved view. This is
// an App-handler workflow, not a claim of native menu execution.
let _ = f.app.update(Message::Select(f.app.project().id));
let _ = f.app.update(Message::Select(f.node));
let current = if different_current_view {
f.target
} else {
f.source
};
if different_current_view {
let _ = f.app.update(Message::Diagram(Choice {
id: current,
label: "Other current view".into(),
}));
}
assert_eq!(f.app.selected, f.node);
assert_eq!(f.app.active_diagram, Some(current));
assert!(
matches!(f.app.draft.as_ref().unwrap().kind, crate::forms::RecordKind::Node(d) if d == f.source)
);
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
let mut expected = before.decoded.project.clone();
let mut placement = expected
.diagrams
.get_mut(&f.source)
.unwrap()
.nodes
.remove(&f.node)
.unwrap();
let value = match field {
"/position/x" => {
placement.position.x = 123.;
"123".to_owned()
}
"/alias" => {
placement.alias = "renamed_placement".into();
placement.alias.clone()
}
_ => {
placement.id = Id::new_v4();
placement.id.to_string()
}
};
let id = placement.id;
let expected_document = relocation_expected(
&f.app,
Command::EditNode {
diagram: f.source,
original: f.node,
node: placement.clone(),
},
);
expected
.diagrams
.get_mut(&f.source)
.unwrap()
.nodes
.insert(id, placement);
assert_eq!(expected_document.project(), &expected);
relocation_field(&mut f.app, field, value);
assert!(f.app.apply(), "{}", f.app.status);
assert_eq!(f.app.project(), &expected);
assert_eq!(f.app.document.bundle(), expected_document.bundle());
assert_eq!(f.app.document.revision(), revision + 1);
assert_eq!(f.app.page, Page::Diagram);
assert_eq!(f.app.active_diagram, Some(current));
assert_eq!(f.app.layer, f.app.project().diagrams[&current].layer);
assert_eq!(f.app.selected, id);
assert_eq!(
f.app.selected_node,
if different_current_view {
None
} else {
Some(id)
}
);
let draft = f.app.draft.as_ref().unwrap();
assert_eq!(draft.original, id);
assert!(matches!(draft.kind, crate::forms::RecordKind::Node(d) if d == f.source));
relocation_source_pair(&f.app);
relocation_history(&mut f.app, &before, &expected_document);
}
}
}
#[test]
fn accepted_relocation_refuses_queued_old_canvas_target_with_feedback() {
for edge_hit in [false, true] {
let mut f = relocation_fixture(Layer::Oa, Layer::Sa);
let before = f.app.document.snapshot();
let expected_document = relocation_expected(
&f.app,
Command::Batch(vec![
Command::EditNode {
diagram: f.source,
original: f.node,
node: f.app.project().diagrams[&f.source].nodes[&f.node].clone(),
},
Command::RelocateNode {
node: f.node,
diagram: f.target,
},
]),
);
let mut expected = before.decoded.project.clone();
let placement = expected
.diagrams
.get_mut(&f.source)
.unwrap()
.nodes
.remove(&f.node)
.unwrap();
expected
.diagrams
.get_mut(&f.target)
.unwrap()
.nodes
.insert(f.node, placement);
let revision = f.app.document.revision();
let captured = f.app.route_context().unwrap();
relocation_field(&mut f.app, "/diagram", f.target);
let message = if edge_hit {
Message::CanvasSelect(captured, f.edge)
} else {
Message::SelectNode(f.other_node)
};
let _ = f.app.update(message);
assert_eq!(f.app.project(), &expected);
assert_eq!(f.app.document.revision(), revision + 1);
relocation_selection(&f.app, f.target, f.node);
assert_eq!(
f.app.status,
"Properties applied. The view changed; select a visible target again."
);
assert!(
f.app.multiselect_snapshot().is_none_or(|snapshot| {
!snapshot.targets().iter().any(|target| {
matches!(target, crate::diagram_usage::Target::Placement(id) if *id == f.other_node)
|| matches!(target, crate::diagram_usage::Target::Edge(id) if *id == f.edge)
})
}),
"the rejected old hit must not enter the destination selection"
);
assert_eq!(expected_document.project(), &expected);
assert_eq!(f.app.document.bundle(), expected_document.bundle());
relocation_source_pair(&f.app);
relocation_history(&mut f.app, &before, &expected_document);
}
}
#[test]
fn rejected_placement_destination_preserves_owner_model_source_and_draft() {
let mut f = relocation_fixture(Layer::Oa, Layer::Sa);
let before = f.app.document.snapshot();
let revision = f.app.document.revision();
relocation_field(&mut f.app, "/diagram", Id::new_v4());
let draft = serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot()).unwrap();
assert!(!f.app.apply());
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());
relocation_selection(&f.app, f.source, f.node);
assert_eq!(
serde_json::to_value(f.app.draft.as_ref().unwrap().recovery_snapshot()).unwrap(),
draft
);
}
+18
View File
@@ -39,6 +39,24 @@ impl Session {
}
}
/// Contextual drawing follows the visible semantic source, not the palette layer.
/// The caller separately validates the menu/context lifetime and pending drafts.
pub fn can_begin(project: &Project, diagram: Id, tool: Tool, source: Id) -> bool {
let Some(diagram) = project.diagrams.get(&diagram) else {
return false;
};
let Some(element) = project.elements.get(&source) else {
return false;
};
diagram.projection.is_architecture()
&& (element.layer() == diagram.layer
|| diagram.projection == oa_core::ProjectionKind::Traceability)
&& Tool::for_layer(element.layer()).contains(&tool)
&& tool.can_start(project, source)
&& (tool != Tool::Allocation || tool.available_in(element.layer(), diagram.projection))
&& visible(&oa_core::projection::project_view(project, diagram.id)).any(|id| id == source)
}
pub fn visible(view: &ProjectedView) -> impl Iterator<Item = Id> + '_ {
view.nodes
.iter()
+9 -6
View File
@@ -993,7 +993,10 @@ impl canvas::Program<Message> for DiagramCanvas<'_> {
world,
));
}
return Some(Action::publish(Message::Select(port.element)).and_capture());
return Some(
Action::publish(Message::CanvasSelect(self.context, port.element))
.and_capture(),
);
}
if let Some(id) = self.hit(view, p) {
let position = self
@@ -1010,7 +1013,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::Select(id)).and_capture())
Some(Action::publish(Message::CanvasSelect(self.context, id)).and_capture())
} else if let Some((id, segment)) = self.hit_edge(&scene.routed, p) {
if id == self.selected
&& self.editable
@@ -1026,7 +1029,7 @@ impl canvas::Program<Message> for DiagramCanvas<'_> {
self.begin_route(state, world, Handle::Bend(index), route);
Some(Action::capture())
} else {
Some(Action::publish(Message::Select(id)).and_capture())
Some(Action::publish(Message::CanvasSelect(self.context, id)).and_capture())
}
} else {
state.pan_start = Some((mouse::Button::Left, p));
@@ -2441,7 +2444,7 @@ mod tests {
.unwrap()
.into_inner()
.0;
assert!(matches!(selected,Some(Message::Select(id)) if id==port.element));
assert!(matches!(selected,Some(Message::CanvasSelect(_, id)) if id==port.element));
let request = canvas.context_request(
&state,
click,
@@ -3054,7 +3057,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::Select(id)) if id==edge)
matches!(pointer(&canvas,&mut state,mouse::Event::ButtonPressed(mouse::Button::Left),at),Some(Message::CanvasSelect(_, id)) if id==edge)
);
assert!(
state.route_drag.is_none(),
@@ -3104,7 +3107,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::Select(id)) if id==edge)
matches!(pointer(&canvas,&mut state,mouse::Event::ButtonPressed(mouse::Button::Left),screen),Some(Message::CanvasSelect(_, 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::Select(id)if *id==output));
assert!(matches!(&messages[0],Message::CanvasSelect(_, 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::Select(id) => Some(*id),
Message::CanvasSelect(_, 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::Select(id)) if *id==output),
matches!(messages.first(),Some(Message::CanvasSelect(_, 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::Select(id)) if id == port.element));
assert!(matches!(press, Some(Message::CanvasSelect(_, 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);
+1 -1
View File
@@ -375,7 +375,7 @@ mod tests {
messages.extend(message);
}
}
assert!(matches!(messages.first(), Some(Message::Select(id)) if *id == port));
assert!(matches!(messages.first(), Some(Message::CanvasSelect(_, 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)
);
+2
View File
@@ -327,6 +327,8 @@ 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. Applying ordinary Properties edits keeps that saved architecture view open. Changing a placement's Diagram explicitly moves it and follows its destination. Changes you make to the view's own perspective or identity still take effect. If the app asks you to select again, choose a visible target.",
"In a mixed traceability view, use Draw from a visible source's canvas menu or the Model explorer sidebar on the Architecture page to connect from that source's OA or SA layer without leaving the view. Choose a highlighted compatible visible target; Escape cancels. The palette offers tools for the view's perspective. Opening an endpoint or selecting a record in the Model explorer sidebar follows that record's perspective. Contextual relationship forms keep the view while opening; applying the reviewed relationship can navigate to its result.",
"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.",
+1 -4
View File
@@ -557,10 +557,7 @@ pub fn popup(app: &App) -> Option<Menu<Message>> {
&format!("Draw {}", tool.label().to_lowercase()),
Message::Connection(crate::connection_edit::Event::Begin(*tool, id)),
app.active_diagram.is_some_and(|diagram| {
crate::connection_edit::visible(&projection::project_view(
p, diagram,
))
.any(|element| element == id)
crate::connection_edit::can_begin(p, diagram, *tool, id)
}),
));
}
@@ -372,3 +372,150 @@ 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;
}
+1 -1
View File
@@ -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. Traceability permits both-layer hidden/category/context references; ordinary projections require the diagram layer. 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. 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["Diagram"]["properties"]["view_options"] = {**ref("ViewOptions"), "default": {}}
def upsert_variant(variants, name, value):
+9 -2
View File
@@ -570,8 +570,15 @@ 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. ``Traceability`` permits
references from both layers; ordinary projections require the diagram's layer.
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.
These presentation choices preserve semantic records, routes, placements and
the automatic port positions derived from unfiltered eligible exchanges.
+20 -2
View File
@@ -409,8 +409,12 @@ 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. All ordinary projections require
these references in the declared diagram layer. A filter never hides the other
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
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
@@ -1073,6 +1077,20 @@ 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
View File
@@ -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. Traceability permits both-layer hidden/category/context references; ordinary projections require the diagram layer. 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. 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."
},
"CommunicationPortChoice": {
"oneOf": [
+26
View File
@@ -1,5 +1,31 @@
# Architectural and product decisions
## Canvas inspection, contextual drawing and explicit relocation — 2026-10-02
An ordinary Properties Apply retains the current Architecture diagram, layer and
projection. An accepted edit to the active Diagram itself uses its resulting
identity, layer and projection. A queued canvas target is revalidated against that retained surface
after Apply; an obsolete target is refused with feedback instead of navigating
or selecting hidden content. Explicit Select/Open still follows the requested
model target. An accepted change to a placement's actual owning diagram is also
explicit navigation: it follows the committed destination and resulting UUID.
Changing only geometry, alias or UUID in the same owning diagram retains the
inspection surface. Invalid or missing ownership never grants this exception.
Contextual Draw uses the invoked menu target as its semantic source and the current
diagram as its editing surface. Shared admission checks the diagram projection,
visibility, family and semantic endpoint roles before arming. Restart clears the
pending source while retaining an admitted tool. Explicit model navigation and
post-review creation navigation retain their separate behavior. This avoids
silently conflating an opposite-layer inspection with a request to change views.
The source repairs at c706 and 6018, the failed c706 workspace attempt, the
full-model/source invariants and corrected Windows acceptance are attributed in
[the navigation checkpoint](navigation-validation-20261002.md). The simpler
blanket surface restoration was rejected when the unchanged relocation test
showed that it hid successful explicit moves. This is bounded Architecture
acceptance; diagram label readability and the complete OA/SA goal remain open.
## Private source composition decision — 2026-09-23
Adopt the opaque validated original-span rename batch as the next private
+73 -34
View File
@@ -1,42 +1,81 @@
# Integrated prerelease source checkpoint — 2026-09-24 UTC
# Navigation contribution checkpoint — 2026-10-02 UTC
This branch assembles peer contributions for the Windows prerelease in issue37.
[PROJECT_GOAL.md](../PROJECT_GOAL.md) remains authoritative. The complete OA/SA
application is unfinished; published previews and finite prototypes are intermediate.
Verified production source **6018ae30b9296b9f6b37d7fc038175080d15fafb** preserves
ordinary opposite-layer Properties inspection and contextual drawing while
following accepted explicit placement relocation. Windows full workspace tests
pass **2,236 / 0 failures / 8 existing ignores**; formatting, strict workspace
all-targets Clippy and matching desktop/server/CLI builds pass. Native creation,
projection/history, corrected relocation and saved-source reopen evidence retain
their actual c706/6018 attribution and independent reviews.
[Source, commands, portable evidence and limits](navigation-validation-20261002.md)
are authoritative. The earlier c706 workspace failure remains recorded.
**The current prerelease is Windows-only. Linux/WSL builds and checks remain
excluded by the user's explicit request, including after networking recovers.**
This is a contribution checkpoint, not the complete OA/SA application or an
integrated Help/Save release. Label ellipsis/readability and broader usability
remain open. **Linux/WSL is still excluded by the user's explicit pause.**
[Issue48](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/48)
coordinates the subsequent verified union with delivered Save/Help; ownership,
live status and release decisions belong there. Existing released versions are
unchanged. Earlier integrated and contribution checkpoints follow with their
original source and acceptance boundaries.
## Assembled source and acceptance boundary
# Verified Windows source checkpoint — 2026-10-01 PDT
Combined code through **4deced53b98cd4d7649ba7aa60fa4b9b30d2f3b8**, plus the
test-only import correction from **7c2b06b63f8a0fe14b3f001996bdf965b36a4e85**, adds the reviewed
Connection/Route early-admission correction to the assembled Scenario/Text,
ordinary/State/Process/Allocation selection, direct drawing, Traceability and
picker changes. The [source handoff](prerelease-integrated-source-20260924.md)
identifies the incorporated commits, exact new test paths and required gates.
The nine event regressions and existing completed-Text-Apply cases are retained.
Direct formatting and whitespace checks pass; this source checkpoint does not
claim combined Cargo, native, packaged or release acceptance. The independent
[event-fix review](reviews/prerelease37-event-admission-20260924.md) found no
further material source defect in its stated scope. A subsequent peer compile
found a missing `projection::` qualification in the new Route test helper;
the current code includes that one-line correction. The original compile failed
before running tests. The first combined compile at `897d9f04` subsequently
found a missing `text_editor` import in the Allocation Text Apply regression;
the current source includes that one-line correction too. Both failures remain
recorded in the source handoff. The subsequent full test run at `8ddfadf4`
failed the mounted State picker fixture: added selection controls invalidated
its canvas-relative clicks. Source successor `a9cd364eacbbffa0693bab05b5a2b65f9d105535`
measures an identified fixed-height picker slot and checks Escape capture,
retaining the exact preservation/reopen/Place/Undo assertions. The source
handoff links the preserved failed and interrupted attempts. Corrected combined
Windows execution and package acceptance remain pending.
This branch combines the released integrated foundation
`715036e132677dd2bc2fb37ed316d93dc9987aec` with compact diagram controls and
contextual Help production source `6e7c4ddaf0775198384a8c4882809ea8e14b2976`,
and the protected Save As destination diagnostic correction tested at
`0e83157e09d52e6152df91208e41693b62da2d46`.
[PROJECT_GOAL.md](../PROJECT_GOAL.md) remains authoritative; complete OA/SA
acceptance is unfinished.
Live build, final-source and publication results belong in
[issue37](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/37).
Human workflows are described in [the preview checklist](../docs/preview-human-checks.md).
Private mixed-parser source is excluded from this application candidate.
**Linux/WSL builds remain excluded by the user's explicit request.**
## Verified capabilities and evidence
The [frozen integrated Windows preview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.integrated.20260924)
provides the accepted 715 foundation and its combined test/package evidence.
It remains unchanged. The older pending-build description previously at the top
of this file is superseded by that published result.
The [compact Help contribution](compact-help-validation-20261001.md) removes
permanent canvas tutorial prose, keeps stable concise selection rows and full
allocation destinations, and supports contextual Help/F1 with keyboard reading,
exact caret/draft preservation and safe gesture retirement. Final focused gates
pass 125 tests; the earlier full-suite result retains its separate source scope.
[Fresh package validation](release-compact-help-validation-20261001.md) verifies
actual extracted desktop/server behavior, Save/Undo/Redo/reopen and complete
source/model oracles. This is a scoped development preview, not complete release
or acceptance of the separately owned follow-up union.
The [scoped compact Help Windows preview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.compact-help.20261001)
is published from evidence checkpoint `f1fdca371394aec1148e67f33f4d8c6b53a0c350`.
Its ZIP, checksum and validation attachment were downloaded and matched after
publication; [publication receipts](experiments/compact-help-20261001/publication.md)
retain the exact asset hashes and provenance.
Dense diagrams, remaining Scenario group adapters, quantities/calculations/
constraints, maintained libraries, broad accessibility/DPI and full OA/SA
acceptance remain unfinished. The independent projection/navigation, outgoing-drag,
icon, grid/performance and State-label contributions are outside this code.
The published compact Help ZIP retains misleading external-change wording for a
protected Save As destination collision. This branch's
[diagnostic correction](save-as-diagnostics-20261001.md) distinguishes existing
protected destination files from observed external edits and explains recovery.
Its 29 targeted checks, strict Clippy, matching GUI build, native normal/minimum
window workflows, eight complete byte checkpoints and parsed-model oracle pass;
independent review finds no material blocker in the bounded contribution.
The [fresh scoped package validation](release-save-diagnostics-validation-20261001.md)
checks the actual extracted correction: accurate protected-destination refusal,
Save All/fresh Save As recovery, actual external-edit protection, fresh reopen,
complete source/model oracles and 23 server assertions. Independent final review
finds no material blocker in this artifact. The [save-diagnostics Windows preview](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/releases/tag/v0.2.0-preview.save-diagnostics.20261001)
is published from evidence checkpoint `e144e32e9984477a258759a9dcc75f6e499fc224`;
[publication receipts](experiments/save-diagnostics-package-20261001/publication.md)
verify its ZIP, checksum and validation attachment. It remains outside the
separately owned follow-up union.
[Issue 46](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/46)
holds integration and delivery status. Live ownership and release coordination
remain in Gitea.
## Contribution-specific verification
+11
View File
@@ -0,0 +1,11 @@
# 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,5 @@
*.json -text whitespace=cr-at-eol
*.http -text whitespace=-blank-at-eol,-blank-at-eof,cr-at-eol
*.log -text whitespace=-blank-at-eol,-blank-at-eof,cr-at-eol
*.arcadia -text whitespace=cr-at-eol
package/** -text whitespace=-blank-at-eol,-blank-at-eof,cr-at-eol
@@ -0,0 +1,15 @@
from pathlib import Path
import hashlib, json, subprocess
root = Path(subprocess.check_output(['git','rev-parse','--show-toplevel'], text=True).strip())
packet = root/'harness/experiments/compact-help-20261001/package'
inventory = json.loads((packet/'evidence-files.json').read_text(encoding='utf-8'))
prefix = packet.relative_to(root).as_posix()
assert {p.relative_to(packet).as_posix() for p in packet.rglob('*') if p.is_file()} == set(inventory)|{'evidence-files.json'}
for relative, expected in inventory.items():
data = (packet/relative).read_bytes()
indexed = subprocess.check_output(['git','show',':'+prefix+'/'+relative], cwd=root)
actual = {'bytes':len(data),'sha256':hashlib.sha256(data).hexdigest()}
assert actual == expected, relative
assert indexed == data, 'Index changes captured bytes: '+relative
assert subprocess.check_output(['git','show',':'+prefix+'/evidence-files.json'],cwd=root) == (packet/'evidence-files.json').read_bytes()
print(json.dumps({'status':'PASS','captured_files':len(inventory),'indexed_inventory_identical':True,'policy':'All captured package bytes preserved by scoped -text attributes'},indent=2))
@@ -0,0 +1,13 @@
# Incremental independent #39 evidence review
No material blocker found in the added warning evidence or current release/checkpoint wording. This supplements the earlier final-evidence review, which covered the previous 133-entry packet; the current working-tree inventory has 140 entries, all independently rechecked against exact byte lengths and SHA256 with zero discrepancies.
Reviewed the copied `read-model-warnings.py`, `model-warnings/{pristine,saved,result}.json` and logs, the existing complete model envelopes, the current release validation, packet README and top of STATE. No compiler, GUI, server launch, Gitea action or tracked-file edit was performed. This review file is the only new artifact.
The added script performs GET validation requests for exactly the two resource IDs recorded by the prior complete-model oracle. Captured responses agree with result.json and show both models valid at revision 1 (separate imported resources). Pristine has only `work_without_behavior_context` for `4149a872-3440-59de-9879-8981e78d27ab`; saved retains that identical warning and adds exactly one warning: `trace_drift`, baseline field, `OaChanged`, trace `5309f955-53fd-5ff4-9c0a-91737e947341`.
Independent full-model equality still holds after changing only Activity `61f7bbfb-74a4-5540-9182-2583dfb27a49` name from `Assess pressure alarm` to `Assess pressure alarm QA LEFT XYRIGHT`. The warned trace points to that activity. Its complete trace record and OA/SA review baseline remain identical; the OA baseline still says `Assess pressure alarm`. Thus the new warning is consistent with the existing trace_drift comparison in `crates/oa-core/src/transitions.rs`, not a silent baseline update or transition reconciliation. Both actual stored envelopes also remain identical to their earlier captured GET envelopes after the read-only validation run.
The new README/release explanation accurately classifies one existing coverage warning plus expected OA name drift. It does not claim warning-free modeling. Current release validation and STATE do not assert that a new tag, release or assets are already published; historical published-release references remain distinct. Publication/readback acceptance and the separate #46 diagnostic correction remain outside this review.
This review checks working-tree inventory bytes. The parent's scoped .gitattributes correction, restaging and indexed-blob/hash comparison are separate preservation gates and are not certified here. No snapshot byte normalization is authorized by this review.
@@ -0,0 +1,8 @@
{
"status": "PASS",
"captured_files": 140,
"indexed_inventory_identical": true,
"policy": "All captured package bytes preserved by scoped -text attributes",
"command": "python harness/experiments/compact-help-20261001/check-indexed-evidence.py",
"scope": "The 140 inventory entries and evidence-files.json were compared to working-tree and indexed blobs before this documentation commit. This check does not establish native acceptance or publication."
}
@@ -0,0 +1,41 @@
# Fresh package verification
This packet verifies the exact scoped Windows development-preview ZIP described
in [the release validation](../../../release-compact-help-validation-20261001.md).
Production gates and earlier native evidence remain separately versioned in
[the contribution record](../../../compact-help-validation-20261001.md).
* [Independent artifact/server review](artifact-review.md): archive, provenance,
runtime/notices/docs, captured HTTP and restart scope, including the corrected
Unicode fixture.
* [First native session](native/review.md) and [fresh reopen](native-reopen/review.md):
actual Home/help/edit/caret/minimum/save/history/reopen operations, original
collision refusal and recovery, screenshots and explicit limits.
* `started.json`/`finished.json` in those native directories record system-only
PATH, unrelated cwd, executable/runtime hashes, exit 0 and empty logs.
* `native-source-existing-save.json` and `native-source-destination-*.json`
record the declared whole-file oracles. `saved-source/` preserves actual
changed source and relocated entry bytes; all other hashes refer to pristine
files distributed in the ZIP. Companion files were copied separately.
* `native-model-oracle/` retains requests/responses and result of parsing the
complete pristine and actual saved bundles using the exact packaged server.
Only the declared Activity name differs in the complete Project. Fresh
independent read-only reconciliation also verifies actual disk/source equality.
* `model-warnings/` records subsequent read-only validation: both models are
valid; the name edit adds the expected OA trace-drift warning to the existing
behavior-coverage warning. Transition reconciliation was not performed.
* `server-smoke/` preserves the first harness fixture encoding issue;
`server-smoke-unicode02/` retains its corrected U+2014/CRLF run. Each preserves
all 11 HTTP exchanges, source files, acknowledged envelope and process results.
Stores are not duplicated; captured envelopes and store hashes are retained.
* `extraction-check.json` covers all 565 extracted files; `package.json` and
`build-evidence.json` identify distinct source roles. Runtime/dependency notice
inventories and their full texts remain inside the actual ZIP.
* [Evidence file hashes](evidence-files.json) bind these exact copied bytes.
Helpers are retained as executed, with their original absolute machine paths.
Adapt paths to a fresh extraction/working directory to repeat a workflow. Do not
overwrite prior attempts. Source checks and package reconstruction parameters
are in the release validation and `docs/windows-packaging.md`. These finite
checks do not establish complete OA/SA, general accessibility, Linux, power-loss
safety or application-managed copying of arbitrary external artifacts.
@@ -0,0 +1,41 @@
# Independent compact Help package and server evidence review
No blocking finding in the reviewed artifact integrity, provenance, notices, or bounded server evidence. This review does not accept the packaged desktop workflow or the complete first release. The reviewer performed read-only byte/JSON/PE checks and inspected existing runtime evidence; no compiler, application launch, GUI action or Gitea mutation was performed. This review file is the only reviewer-created artifact.
## Exact package and integrity
Reviewed `ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm.zip`: 34,184,534 bytes, SHA256 `18af75c7aea60bb0d5db5b1925d0d0815ecd08afc5bb258438851cfda809a6f2`.
Independent checks verified all 565 ZIP entries against extracted bytes, the exact extracted file set, 564 SHA256SUMS entries and all 559 package input hashes. Archive names have no absolute/traversal paths, backslash ambiguity, colon paths or case-insensitive duplicates. Package BuildEvidence equals the supplied evidence file. Independent PE parsing reproduces all three recorded import lists and finds no delay-import directory. Dynamic runtime behavior remains a separate check.
Provenance correctly distinguishes desktop source `6e7c4ddaf0775198384a8c4882809ea8e14b2976`, reused server source `715036e132677dd2bc2fb37ed316d93dc9987aec`, and package documentation source `ea23e229260d7d050b3c3b88e77c3a851e936abe`. It identifies Rust 1.98.1, gnullvm and dev/unoptimized profile. Executable hashes match the recorded final desktop (`d55ecad4...`) and released server (`6cebacc1...`). The source comparison has no core, server, Cargo or vendor dependency changes; the documentation commit changes only acceptance evidence. These are checked provenance records, not an independent rebuild.
## Runtime, notices and documentation
The selected DLL is `f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4`, matching the final desktop runtime and the selected LLVM-MinGW 20260826 x86_64 toolchain. Its packaged distribution LICENSE matches that toolchain exactly. The component notice matches the reviewed manifest's DLL-bound entry and exact LLVM commit `ea7d852a70e8bdfaf601d6626a760f9771b2c4b4`; distribution build correspondence remains the stated external provenance limitation.
All 489 referenced notice hashes pass. The 268 dependency package identities, registry checksums, features and local source-hash inventories agree with the accepted donor inventory except for intended `oa-desktop` source changes. Rust notice/target-library inventory is identical. Cargo.lock and all seven shared workspace-input hashes match the checkout. This is notice/inventory verification, not a universal license or binary asset audit.
All 66 collected source documentation/example/deployment/license files match the checkout. The package document checker resolves all 18 local links. Built-in Help execution is outside this static check.
## Fresh server pairing evidence
The donor server's old packaged runtime was `aa282fa4...`; its historical runtime acceptance does not establish the new pairing. The fresh smoke pins the server and `f45604a1...` DLL, uses the newly extracted package, an unrelated working directory, a fresh store, loopback port 54695, and a system-only child PATH. Both process command lines point to this extracted server.
The reviewer compared the adapted harness with the prior accepted harness and independently checked all 11 captured HTTP request/response pairs. The 23-check result is supported: readiness; exact packaged OpenAPI; source import and readback; stable model IDs distinct from resource identity; CRLF and comment preservation; a name-only complete-model change from revision 1 to 2; changed ETag; stale If-Match refusal with 412/revision_conflict; unchanged accepted model/source after refusal; exact committed envelope; and identical envelope, source, ETag and stored bytes after restart. The store hash is `62d1fed82f4fc793f3173beb8693fbde94d6aabf4e38e161da93b59178969ee2`. Logs contain only expected startup messages; stdout is empty.
The first run contains a harness-only fixture encoding error: adaptation changed the old em dash comment to the literal mojibake text `—` (U+00E2, U+20AC, U+201D). Its exact current UTF-8 input/output comparison remains valid, but it did not exercise the intended em dash. The original run and its bytes remain preserved.
The parent then ran `server-smoke-unicode02` with a fresh store and processes. Independent script comparison shows only the fixture comment changed to U+2014; runtime pins and behavioral assertions are unchanged. The reviewer independently reconciled this run's 11 captured HTTP exchanges, exact original/edited source bytes and CRLF, complete model delta, revision 1 to 2, ETags, 412 refusal, stored envelope and exact restart readback. All checks agree with its 23-check PASS result. The corrected source contains exactly one non-ASCII codepoint, U+2014. The new resource is `53a28b61-1736-4bc4-8553-28995488647f`; its revision-1 and revision-2 ETags match that resource. The package ZIP remains unchanged.
| Evidence | First run (`server-smoke`) | Corrected run (`server-smoke-unicode02`) |
| --- | --- | --- |
| `run.py` SHA256 | `41d25f1d63b32737859674f414470c06868948db60f29a63eb5ac6b6857addb3` | `20344608a0623584a0e0ac66dc7f435a0de053d39b4254e92f1c1148aed8e8b9` |
| `original.arcadia` SHA256 | `c40896187097d97830b807e993aff39a93b62a1824abc46760c3b487767fa3f5` | `1f6b88cbcc3ac12995192feca91e07b9f6d536f2d21d0df6f42aa08f5c745ba9` |
| Committed store SHA256 | `62d1fed82f4fc793f3173beb8693fbde94d6aabf4e38e161da93b59178969ee2` | `f615fd9d08ed4e0bb4f2b7b13fa52095959e34c73ac3498487a8544d23eb13fb` |
Each run's two owned server processes were stopped with Windows TerminateProcess after acknowledged requests, producing exit code 1. This supports restart persistence of acknowledged writes, not graceful shutdown, in-flight-write recovery or power-loss safety. Authentication, remote/TLS behavior, concurrency/load and wider API semantics were not exercised.
## Acceptance boundary
Fresh packaged desktop edit/save/undo/redo/reopen and contextual Help checks remain owned by the native reviewer. Publish their actual results separately. Retain development-preview limitations, the distinction between final focused checks and inherited full-suite evidence, and the outstanding complete-project requirements. Existing published assets remain unchanged.
@@ -0,0 +1,58 @@
{
"source_identity": "Desktop: 6e7c4ddaf0775198384a8c4882809ea8e14b2976; unchanged reused server: 715036e132677dd2bc2fb37ed316d93dc9987aec; package documentation: ea23e229260d7d050b3c3b88e77c3a851e936abe",
"profile": "dev, unoptimized, debug assertions enabled",
"compiler": "rustc 1.98.1 (48a229cea 2026-09-01)",
"target": "x86_64-pc-windows-gnullvm",
"binary_sha256": {
"arcadia-studio.exe": "d55ecad4b0f75c0c9c26625e5441bef9c9360890b65bc13236c6ebfaf03d631e",
"arcadia-server.exe": "6cebacc117e46fdab023c6402a5cebd6929fb55993ae8ff6b89cd1c6c1170237"
},
"binary_origins": {
"arcadia-studio.exe": {
"source": "6e7c4ddaf0775198384a8c4882809ea8e14b2976",
"build_command": "cargo build --locked -p oa-desktop --bin arcadia-studio",
"evidence": "harness/experiments/compact-help-20260925/candidate-6e7c/build.log",
"native_evidence": "harness/compact-help-validation-20261001.md"
},
"arcadia-server.exe": {
"source": "715036e132677dd2bc2fb37ed316d93dc9987aec",
"reuse": true,
"release": "v0.2.0-preview.integrated.20260924",
"original_build_evidence": {
"source_identity": "715036e132677dd2bc2fb37ed316d93dc9987aec",
"profile": "dev",
"target": "x86_64-pc-windows-gnullvm",
"compiler": "rustc 1.98.1 (48a229cea 2026-09-01)",
"binary_sha256": {
"arcadia-studio.exe": "2c03bd6cc3f1110c219e4f57ab94874a121590f83779239a93289b7170664399",
"arcadia-server.exe": "6cebacc117e46fdab023c6402a5cebd6929fb55993ae8ff6b89cd1c6c1170237"
},
"mandatory_gate": {
"path": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\prerelease-gates-v2-20260924\\final-04\\summary.json",
"bytes": 10594,
"sha256": "8b655c429b4abdeec0476a8397565fcd07682642f1d7b87e9a0c4eec63e22fc9"
},
"source_manifest": {
"path": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\prerelease-gates-v2-20260924\\final-04\\inputs-before.json",
"bytes": 1740725,
"sha256": "38e4f7d37b67b06b7991d48560c5bd56ec5893909f3cffd224442d15bdc35f88"
},
"environment": {
"path": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\prerelease-gates-v2-20260924\\final-04\\environment.json",
"bytes": 8981,
"sha256": "d9d89bc52a9312044e277f92b1585350fc86d5cc963dd91a39c669bc6ca2f39f"
},
"artifact_relocation": {
"path": "C:\\Users\\User\\Documents\\Git\\ARCADIA-Studio\\.local\\prerelease-gates-v2-20260924\\final-04\\artifact-relocation.json",
"bytes": 6181,
"sha256": "3298225b9890aab1a8029a4d6dff42a8162f8d77f2c71bcdd379d25ebcb1f9d6"
},
"scope": "Exact preserved CargoJSON-identified individual product builds from final04; supplemental combined build is separately recorded. Native/package acceptance is separate."
},
"unchanged_input_check": "git diff --exit-code 715036e132677dd2bc2fb37ed316d93dc9987aec ea23e229260d7d050b3c3b88e77c3a851e936abe -- crates/oa-core crates/oa-server Cargo.toml Cargo.lock"
}
},
"package_documentation_source": "ea23e229260d7d050b3c3b88e77c3a851e936abe",
"runtime_sha256": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4",
"acceptance_scope": "Contribution gates and prior released-server evidence are separate from fresh packaged desktop/server checks; this is an unfinished Windows development preview, not complete OA/SA acceptance."
}
@@ -0,0 +1,18 @@
from pathlib import Path
import hashlib, json, sys
root = Path(__file__).resolve().parent
bundle = Path(json.loads((root/'extraction-check.json').read_text(encoding='utf-8'))['bundle'])
before = bundle/'examples/release1'
after = root/'working/saved'
expected = {p.relative_to(before).as_posix():p.read_bytes() for p in before.rglob('*') if p.is_file()}
name='oa/activities/activity_oa_restore_work.arcadia'
needle=b'oa_activity oa_assess "Assess pressure alarm" id "61f7bbfb-74a4-5540-9182-2583dfb27a49"'
assert expected[name].count(needle)==1
expected[name]=expected[name].replace(needle,needle.replace(b'"Assess pressure alarm"',b'"Assess pressure alarm QA LEFT XYRIGHT"'))
expected['qa-saved.arcadia']=expected.pop('project.arcadia')
actual={p.relative_to(after).as_posix():p.read_bytes() for p in after.rglob('*') if p.is_file()}
diff=[p for p in sorted(set(expected)|set(actual)) if expected.get(p)!=actual.get(p)]
report={'status':'PASS' if not diff else 'FAIL', 'files':len(actual), 'source_files':sum(p.endswith('.arcadia') for p in actual), 'oracle':'Complete pristine37-file project with entry relocated to qa-saved.arcadia and exactlyone declared Activity-name literal; seven non-source companions separately copied before SaveAs,30 sourcefiles written by application.', 'differences':diff, 'sha256':{p:hashlib.sha256(v).hexdigest() for p,v in actual.items()}}
with (root/('native-source-destination-'+sys.argv[1]+'.json')).open('x',encoding='utf-8') as f: json.dump(report,f,indent=2)
print(json.dumps({k:v for k,v in report.items() if k!='sha256'},indent=2))
assert not diff, diff
@@ -0,0 +1,562 @@
{
"artifact-review.md": {
"bytes": 6484,
"sha256": "f76c089c5dd90785627b4fb73f5fa167b4cf6084aa3e163bb8f8f383f1e7129c"
},
"build-evidence.json": {
"bytes": 3570,
"sha256": "3b278a3401b9a4c2de0538d70f603c3c039b308d5d2952ebed792efcac3e08e5"
},
"check-saved-destination.py": {
"bytes": 1595,
"sha256": "fec0e28fb2e9b45d5de408c5cba5b8600f58d07baec5545ee96e0c73b880f5be"
},
"extract-check.py": {
"bytes": 1453,
"sha256": "53399ab54b12b11d5057d0e19d044b242094be31adc8362321b2adace4704182"
},
"extraction-check.json": {
"bytes": 73193,
"sha256": "2de063bcee74e2ffd7e70dbba717f4615932a2f0f3024a90e87406f17ab12244"
},
"final-evidence-review.md": {
"bytes": 3287,
"sha256": "cd86306f43c8337c087601c8248acc7c13de959bf974d9b3da61ce725d146c0a"
},
"graphics-environment.json": {
"bytes": 95,
"sha256": "1a96358d1bc719c4bcbb4d61f09414d83d6f26aaa2319c84ce270c0c8510b8b6"
},
"launch-native-reopen.py": {
"bytes": 1957,
"sha256": "1917b02f25315c4be06b23b486263098314173cb956f1cb3a3b70d9e3813cffc"
},
"launch-native.py": {
"bytes": 2288,
"sha256": "551b15c6a9fa48867d17f3080ae0a52e21290530fea403890b4cfafe1a627ca4"
},
"native-model-oracle.py": {
"bytes": 3894,
"sha256": "a7c3007becaba4eedb1507e58b33143163cd3e9a4892c8d2e8a16fccff493cae"
},
"native-source-destination-initial.json": {
"bytes": 4373,
"sha256": "e54ea36ebf4c22e169a91f10acb4cf82058c37882f7aa431d84f14ff0c228709"
},
"native-source-destination-reopen.json": {
"bytes": 4373,
"sha256": "e54ea36ebf4c22e169a91f10acb4cf82058c37882f7aa431d84f14ff0c228709"
},
"native-source-existing-save.json": {
"bytes": 4261,
"sha256": "b731d9e3dd24cafa0e1120ecc9eba4f308333d40ff03ec9cd5dc0d0974667cb6"
},
"package.json": {
"bytes": 78341,
"sha256": "36bcff8a760c08674f3fc017bb5affc8a5f4f39174ac9d5a29298f2423155832"
},
"packaging.log": {
"bytes": 427,
"sha256": "e4c27a29f3f09239691242290fb491c80b65d743be7ce7aa24e203a0086152dd"
},
"read-model-warnings.py": {
"bytes": 1847,
"sha256": "3cd673212995a89ba72aeea2ca97cef08f3d58be0b895dcae009ef27bd4f6acb"
},
"README.md": {
"bytes": 2876,
"sha256": "1932762f007d0da37c8b3521c0ae301c62d817a745ece29b06dd1c5f1fc4f19b"
},
"windows-environment.json": {
"bytes": 133,
"sha256": "9adf19bddb99b0a30a42f6e13b47c21bbbe083ed31ddd3760acf2b9411e13546"
},
"model-warnings/pristine.json": {
"bytes": 276,
"sha256": "763972bac5b1e0e094321fbaf2ad6c52f91d33e4a4de341674863bc932ddee10"
},
"model-warnings/result.json": {
"bytes": 1805,
"sha256": "5c59af1af38acb9a675f5673c7fdc61eead6b5dd870c7bc0b5e6252690abfe7f"
},
"model-warnings/saved.json": {
"bytes": 498,
"sha256": "369a1f7a983b397c47c23294136dfa05a10723397d5be38d2917174111e7fde1"
},
"model-warnings/stderr.log": {
"bytes": 122,
"sha256": "c973e78913b952a8feb536d57ada913ec6b2305666394b3245f56d0a89f0ac64"
},
"model-warnings/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"native/01-package-home.png": {
"bytes": 117323,
"sha256": "9c23b3474cf1249d20b06829827a6c169fc0c5fb2dad2c913fd7bc332c94d8df"
},
"native/02-bundled-walkthrough.png": {
"bytes": 141845,
"sha256": "d54ffa47f509f7410f4c01fe330519fc7a593968034b80ee4884e4bb7ae36a09"
},
"native/03-bundled-offline-help.png": {
"bytes": 197540,
"sha256": "a6b476a040f7561095158b216ee84f1469fe79a321211c20f10d324c7a0bb417"
},
"native/04-working-minimum.png": {
"bytes": 110401,
"sha256": "a622d6cc7f6d597a902eb391eed66b084d12f591cd475cf7d866576262b4ff2c"
},
"native/05-minimum-help-keyboard.png": {
"bytes": 147058,
"sha256": "3d2eb74ddb0a07618569c6207245ab9e7c67af93511bb01db172b4bffc2c4dcd"
},
"native/06-exact-draft-left-xyright.png": {
"bytes": 102437,
"sha256": "ba04a51628b6fca5d1fd64881b8c56b3982dc5b40da40799d017381ea8cc263c"
},
"native/07-qa-rename-applied.png": {
"bytes": 104209,
"sha256": "0e179a0c7b1a772330d958cf17d5de0120825b8941ba925864b9456bbb05a226"
},
"native/08-save-as-path.png": {
"bytes": 67205,
"sha256": "c7e85bc22e90d74d656fb6a5655ee9e45075f2acdbfa5109a4a764c80be404ad"
},
"native/09-save-as-unexpected-external-change.png": {
"bytes": 106673,
"sha256": "95d14fdde47c8ca341e6e78c1238572abcd973cb90274cf7d2806222e1a0f58b"
},
"native/10-save-existing-project-success.png": {
"bytes": 103843,
"sha256": "f7d24e57ba1923d60c8d62705a97e27bcf8f67a2e446fe55335c8f6840f06a92"
},
"native/11-undo-original-unsaved.png": {
"bytes": 103153,
"sha256": "ef8701270cb33e0a46d8f571e54c2f112ca736e8080bc1809e0fb93e791cdddf"
},
"native/12-redo-renamed-saved.png": {
"bytes": 104103,
"sha256": "de57390746da695ede2f09459b5013cdd1096cbd3468bbd68318730598fc0bb4"
},
"native/13-save-after-redo.png": {
"bytes": 103955,
"sha256": "8b52efae73d8484a75f446b54283db37bb16a424a86dc74a45b832aebfa7bfa6"
},
"native/14-fresh-save-as-path.png": {
"bytes": 67020,
"sha256": "f342f3b2aaad16fce04ed77fec04f2af01170b7ab23f77b030a1a4e8100e8df6"
},
"native/15-fresh-save-as-success.png": {
"bytes": 104098,
"sha256": "a33f18489f049fcebf521e1071558efccb5bdb31094db88145ccca40529bc348"
},
"native/baseline.json": {
"bytes": 3960,
"sha256": "3be898f41d6692d6538ea771b3e2533bc5dd12300cd48e61368dfa62fa843b07"
},
"native/finished.json": {
"bytes": 855,
"sha256": "126e45d0832a532fbfc8ec29679c3157f3916d24a590099886883daa27354628"
},
"native/review.md": {
"bytes": 6995,
"sha256": "8eaf19212153338e954b1970b978f2fa1a368db5caf072ced67083e87c553a9c"
},
"native/started.json": {
"bytes": 705,
"sha256": "fea81d1cdd8475aa531f5a2c634d0368a69ac97238c390ff84ada1b12c670b85"
},
"native/stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"native/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"native-model-oracle/01.request.json": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"native-model-oracle/01.response.json": {
"bytes": 15,
"sha256": "a29ee2b15c494311c52521766e44af56a3ad2248e7a8ab465e5206463c13d288"
},
"native-model-oracle/02.request.json": {
"bytes": 73418,
"sha256": "b82d1eb074d86d60e44651d17f659af7202fadf65bb961ecb0a57535af876b5a"
},
"native-model-oracle/02.response.json": {
"bytes": 245,
"sha256": "28f6def33199309654724c1dd75f4a7c1b3bbbe4e8a02f016a172418357c61a9"
},
"native-model-oracle/03.request.json": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"native-model-oracle/03.response.json": {
"bytes": 164079,
"sha256": "f6d9eb934064ab1bdd2135a85982be0b4367a74ac2943f0dcf5dbf02023816e1"
},
"native-model-oracle/04.request.json": {
"bytes": 73436,
"sha256": "ea27de70d45ee592ce37ba315e18884e252e540af0731b0106dbe3bc48784de0"
},
"native-model-oracle/04.response.json": {
"bytes": 245,
"sha256": "79a6f31fabe5aa7bcb9dbf79046a967f1de8fbabcb86b2bfeb6a366f7c13df7f"
},
"native-model-oracle/05.request.json": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"native-model-oracle/05.response.json": {
"bytes": 164113,
"sha256": "1c0780522bb638cef226c97235c072e47be2aba2a37929f627ffbcda297593cd"
},
"native-model-oracle/result.json": {
"bytes": 1139,
"sha256": "46b8fb4a5696fb523342d8b705e2aa1f26f545ebdaf580094b5b2a82fcbf79ee"
},
"native-model-oracle/stderr.log": {
"bytes": 122,
"sha256": "9cd9490761f6fabe3ff41d81abe44dfe4e3b8c835372b6cc8596497b7363ab72"
},
"native-model-oracle/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"native-reopen/01-reopen-exact-path.png": {
"bytes": 73428,
"sha256": "a3b79794c1621032652e2ced9973bf52c95f57dea8d531ffe11d1fcf3adaf261"
},
"native-reopen/02-reopened-exact-name-properties.png": {
"bytes": 141025,
"sha256": "2aea0a42d54b529b5f6d1ea1d7df1659b6f3824d3567cb01c52a4d472d9b6bbc"
},
"native-reopen/03-reopened-help.png": {
"bytes": 193670,
"sha256": "8ce13ea7bf4af86136d98d77e4294bf984d33901dea06eb8a9f785bec5d3433d"
},
"native-reopen/04-reopened-no-change-save.png": {
"bytes": 139742,
"sha256": "b3b550c7ad0937bf99c1a269d1bf0021d71588731fe0407c3439d210c85cfdce"
},
"native-reopen/finished.json": {
"bytes": 855,
"sha256": "979e38ad7e5a96fa82cca64b35a567fc85de695264ac633bb8b0443eefaac9a9"
},
"native-reopen/review.md": {
"bytes": 2668,
"sha256": "9a90bd3819150695d3a412cc426cf4123da9fa5b01b0b5c49a9b6bfcef44c352"
},
"native-reopen/started.json": {
"bytes": 705,
"sha256": "376c25fdf50d8e51d4d906650a5c8e361729ca004b0ad0c2b8d02e575c51e5db"
},
"native-reopen/stderr.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"native-reopen/stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"saved-source/qa-saved.arcadia": {
"bytes": 1958,
"sha256": "b2674e6bf77cfe0f6f0ba79c81d2e96d92f39c02ca2c6266d80e91723bfb9fda"
},
"server-smoke/01.request.http": {
"bytes": 72,
"sha256": "d178bdd2c015230bb3c464f4f2c40f0332aebd3a1836d1b30c3575a666927ed6"
},
"server-smoke/01.response.http": {
"bytes": 219,
"sha256": "c9837f66841693496fe43245a001510a6db38db9b42ed5a0ba497f1010b47841"
},
"server-smoke/02.request.http": {
"bytes": 79,
"sha256": "dbafc7aef09e87f06586df41c4c9f9c9e98bfafe2faf5445e0886471067930b6"
},
"server-smoke/02.response.http": {
"bytes": 349010,
"sha256": "e3907515ac91e9083a1252e1ee6c9089693ca43eaa1f2415bf52cb3207cf9427"
},
"server-smoke/03.request.http": {
"bytes": 479,
"sha256": "6ba9b5f15b32786dc8eca23bac0d85a90c23267d7576f770637aff5c2ace72b9"
},
"server-smoke/03.response.http": {
"bytes": 535,
"sha256": "f59203c43f1e837b734d3f922b327ce4d24cecf58ed19c21d6dce81554d0538a"
},
"server-smoke/04.request.http": {
"bytes": 112,
"sha256": "51d2e62c3de3198b40fa674942f07921403e4cbe89546b6eb4e20229050cb250"
},
"server-smoke/04.response.http": {
"bytes": 1075,
"sha256": "0ceb40c589e88258948e6e01ec09b1413c789f05178b568e9a00ccbafe747eea"
},
"server-smoke/05.request.http": {
"bytes": 127,
"sha256": "a85dc8166e419ffb881812a48d538de5f44e4966915de275233e9ae8dddebcf6"
},
"server-smoke/05.response.http": {
"bytes": 584,
"sha256": "d4a774e147a81c9b50fc010b37eb2d7ffbc712f196757fd38d24feae05f91ef4"
},
"server-smoke/06.request.http": {
"bytes": 561,
"sha256": "b8559c9d057ac3bbd89ff31825edd912cb838e816542ba5d511ce42813ca33f7"
},
"server-smoke/06.response.http": {
"bytes": 1224,
"sha256": "43da1d02bcdd7f8405ef27250209548406de1fef3556ed9b60d545baa2d0bc69"
},
"server-smoke/07.request.http": {
"bytes": 566,
"sha256": "29c3b3e2a29542ec5b31ff3a3dbde889333b99ba1faa6e119844687cc31e89ee"
},
"server-smoke/07.response.http": {
"bytes": 450,
"sha256": "6f4491155694d2e354c4bb4d00652619b0a2b12cc450773a86be5dfcd558633e"
},
"server-smoke/08.request.http": {
"bytes": 112,
"sha256": "51d2e62c3de3198b40fa674942f07921403e4cbe89546b6eb4e20229050cb250"
},
"server-smoke/08.response.http": {
"bytes": 1071,
"sha256": "e17a54771e198300c9b0bc641f4f866414425f872df3707ee67bc00dcbe53c2d"
},
"server-smoke/09.request.http": {
"bytes": 72,
"sha256": "d178bdd2c015230bb3c464f4f2c40f0332aebd3a1836d1b30c3575a666927ed6"
},
"server-smoke/09.response.http": {
"bytes": 219,
"sha256": "96bd3e4e429a6e4213f2197ef5a287eee0104f62d1c74e3df4a89200076f1a1a"
},
"server-smoke/10.request.http": {
"bytes": 112,
"sha256": "51d2e62c3de3198b40fa674942f07921403e4cbe89546b6eb4e20229050cb250"
},
"server-smoke/10.response.http": {
"bytes": 1071,
"sha256": "f4be9314bc570fa4381fdea263d80cd873c426e768ceef72d252c90a03c3fb81"
},
"server-smoke/11.request.http": {
"bytes": 127,
"sha256": "a85dc8166e419ffb881812a48d538de5f44e4966915de275233e9ae8dddebcf6"
},
"server-smoke/11.response.http": {
"bytes": 582,
"sha256": "5f0836381a9a8e4835df8e6ac78e863c5058a80445062de949b2ba8e49463fad"
},
"server-smoke/accepted-envelope.json": {
"bytes": 1056,
"sha256": "b759db49fc9a05a1fec29182f07ec0fe3d22ce8fe3e9b808f97c58738a8affbf"
},
"server-smoke/edited.arcadia": {
"bytes": 254,
"sha256": "fe9b95bc5171dbfc018ccbad26a82d24a65df6554bfdf03f3f634ff5b8b0d267"
},
"server-smoke/first.stderr.log": {
"bytes": 115,
"sha256": "75300bf24bdfebe3393f4bfbe910423ce1b67ed8b9d53874c30fa2b8af6a4326"
},
"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": 256,
"sha256": "c40896187097d97830b807e993aff39a93b62a1824abc46760c3b487767fa3f5"
},
"server-smoke/package-provenance.json": {
"bytes": 78340,
"sha256": "22bd0d7bc5c5b3d31b48e87d252e75b16a44c057b17a68a7c64ca52b47ef16e3"
},
"server-smoke/restart.stderr.log": {
"bytes": 115,
"sha256": "75300bf24bdfebe3393f4bfbe910423ce1b67ed8b9d53874c30fa2b8af6a4326"
},
"server-smoke/restart.stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"server-smoke/result.json": {
"bytes": 4620,
"sha256": "b3678a31f08776f131c1e6f0e1fbdb97c30918e1a0e0620cd23ae2c75fb2bac0"
},
"server-smoke/run.py": {
"bytes": 9050,
"sha256": "41d25f1d63b32737859674f414470c06868948db60f29a63eb5ac6b6857addb3"
},
"server-smoke/version.stderr.txt": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"server-smoke/version.stdout.txt": {
"bytes": 21,
"sha256": "af82a1126f98794babaa173139764cadbfb5143a235f9a887ef70d18d00a0d91"
},
"server-smoke-unicode02/01.request.http": {
"bytes": 72,
"sha256": "9889799d3c78a1c2c8761714fcdb086fea1574580ba501ba5e59435408fb3a8c"
},
"server-smoke-unicode02/01.response.http": {
"bytes": 219,
"sha256": "f8b3f06d619ef978d0004c8bdc6f92c41c1aeb8be5b4199db1f3a418d207e182"
},
"server-smoke-unicode02/02.request.http": {
"bytes": 79,
"sha256": "8e10577e77b978b143209fc185f83d0f19f7185475883094e9e32784fdc96544"
},
"server-smoke-unicode02/02.response.http": {
"bytes": 349010,
"sha256": "8cb865488944b5eae7cf223abdc384609bdc63f2534dffb455e17bdc5e0e9075"
},
"server-smoke-unicode02/03.request.http": {
"bytes": 474,
"sha256": "02d64c95a818d10f077e3d68bdefe8eaa0a2196e906de97c41844d37229a4232"
},
"server-smoke-unicode02/03.response.http": {
"bytes": 535,
"sha256": "8c3dd51bb7a0b63f7e7aabd4f826805575e5b5b59c8f5685768f22452ddbc0d9"
},
"server-smoke-unicode02/04.request.http": {
"bytes": 112,
"sha256": "459d1577ff58f571a7293b1633482219c386bb3148452cdadb9adbf83c327ca6"
},
"server-smoke-unicode02/04.response.http": {
"bytes": 1070,
"sha256": "cfb0007987cbb4aa45e4fb361b3342e94dda771130d7dfbcd3025ac174764bf0"
},
"server-smoke-unicode02/05.request.http": {
"bytes": 127,
"sha256": "2a8753e21da74c0881d33aa5c39ce3f46d67f48a2fb14a27a251f162a3a1008c"
},
"server-smoke-unicode02/05.response.http": {
"bytes": 579,
"sha256": "c63db6bae24c2930650701ae8cab13efa085a12a372be707efdf776dd2422c20"
},
"server-smoke-unicode02/06.request.http": {
"bytes": 556,
"sha256": "06bfc2c585be313cf79985a38cb761ba66344b031cecc5e5231fddca06eacf52"
},
"server-smoke-unicode02/06.response.http": {
"bytes": 1219,
"sha256": "a3d1a29ce25e2cd5b97d485fe61aa0199db810c3e38ad6369bfb27e38e2987d3"
},
"server-smoke-unicode02/07.request.http": {
"bytes": 561,
"sha256": "625b07333f1fae8ebbba716b90dd8b94a9d83e635acc0e91c7e5aeff9e1e8a96"
},
"server-smoke-unicode02/07.response.http": {
"bytes": 450,
"sha256": "fd1ce539aac069050046d05a47326a3301859b291473320d811f3570a6b71629"
},
"server-smoke-unicode02/08.request.http": {
"bytes": 112,
"sha256": "459d1577ff58f571a7293b1633482219c386bb3148452cdadb9adbf83c327ca6"
},
"server-smoke-unicode02/08.response.http": {
"bytes": 1066,
"sha256": "e46cb3c72dac4f265b3057c47db15c9fd7f31cadff4b44dc894054775956cfdd"
},
"server-smoke-unicode02/09.request.http": {
"bytes": 72,
"sha256": "9889799d3c78a1c2c8761714fcdb086fea1574580ba501ba5e59435408fb3a8c"
},
"server-smoke-unicode02/09.response.http": {
"bytes": 219,
"sha256": "27d4e74daec79075c740012c96d87207d06be75c0c734864fb9aeea9e8558ad4"
},
"server-smoke-unicode02/10.request.http": {
"bytes": 112,
"sha256": "459d1577ff58f571a7293b1633482219c386bb3148452cdadb9adbf83c327ca6"
},
"server-smoke-unicode02/10.response.http": {
"bytes": 1066,
"sha256": "563bf6f460fb462e2a034b13485531e0475027947162ac22d1c10d39d82ac4f5"
},
"server-smoke-unicode02/11.request.http": {
"bytes": 127,
"sha256": "2a8753e21da74c0881d33aa5c39ce3f46d67f48a2fb14a27a251f162a3a1008c"
},
"server-smoke-unicode02/11.response.http": {
"bytes": 577,
"sha256": "5ab73722cc5199465aef08b1e364b744bbd2f3545d522c989a10807a9a552da4"
},
"server-smoke-unicode02/accepted-envelope.json": {
"bytes": 1051,
"sha256": "aaf8226a9201861d4aa31fe074e85b5f61887f2494154d72804bc4ffd0cbb265"
},
"server-smoke-unicode02/edited.arcadia": {
"bytes": 249,
"sha256": "e59acb5ed01e55057096b22a39667b40ddddb97227395d04c281686a73c6a14c"
},
"server-smoke-unicode02/first.stderr.log": {
"bytes": 125,
"sha256": "5274d3b78e630e5bcff94b78918123440c150ba62f4cea555cf74bfb151a52c6"
},
"server-smoke-unicode02/first.stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"server-smoke-unicode02/help.stderr.txt": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"server-smoke-unicode02/help.stdout.txt": {
"bytes": 350,
"sha256": "99c5cb07d451c6da829b835b66fce92ce5c2529508c8053030123df704d0610e"
},
"server-smoke-unicode02/original.arcadia": {
"bytes": 251,
"sha256": "1f6b88cbcc3ac12995192feca91e07b9f6d536f2d21d0df6f42aa08f5c745ba9"
},
"server-smoke-unicode02/package-provenance.json": {
"bytes": 78340,
"sha256": "22bd0d7bc5c5b3d31b48e87d252e75b16a44c057b17a68a7c64ca52b47ef16e3"
},
"server-smoke-unicode02/restart.stderr.log": {
"bytes": 125,
"sha256": "5274d3b78e630e5bcff94b78918123440c150ba62f4cea555cf74bfb151a52c6"
},
"server-smoke-unicode02/restart.stdout.log": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"server-smoke-unicode02/result.json": {
"bytes": 4641,
"sha256": "f72d00454f1e3d7051b6158447860b00538a9ca4c2812ab2c8e5ce54a00b4b1a"
},
"server-smoke-unicode02/run.py": {
"bytes": 9045,
"sha256": "20344608a0623584a0e0ac66dc7f435a0de053d39b4254e92f1c1148aed8e8b9"
},
"server-smoke-unicode02/version.stderr.txt": {
"bytes": 0,
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"server-smoke-unicode02/version.stdout.txt": {
"bytes": 21,
"sha256": "af82a1126f98794babaa173139764cadbfb5143a235f9a887ef70d18d00a0d91"
},
"saved-source/oa/activities/activity_oa_restore_work.arcadia": {
"bytes": 1146,
"sha256": "131451be326e44ccf022996246cc341a32040c7ac6f56b4db4202e6c259cfc0a"
}
}
@@ -0,0 +1,27 @@
from pathlib import Path
import hashlib, json, zipfile
root = Path(__file__).resolve().parent
archive = next((root / 'dist').glob('*.zip'))
expected = (Path(str(archive)+'.sha256')).read_text().split()[0]
assert hashlib.sha256(archive.read_bytes()).hexdigest() == expected
target = root / 'extracted'
target.mkdir(exist_ok=False)
with zipfile.ZipFile(archive) as z:
assert len(z.namelist()) == len(set(z.namelist())) == 565
for name in z.namelist():
p = Path(name)
assert not p.is_absolute() and '..' not in p.parts
z.extractall(target)
bundle = next(target.iterdir())
manifest = {}
for line in (bundle / 'SHA256SUMS.txt').read_text().splitlines():
digest, name = line.split(' ', 1)
assert name not in manifest
assert hashlib.sha256((bundle / name).read_bytes()).hexdigest() == digest, name
manifest[name] = digest
names = {p.relative_to(bundle).as_posix() for p in bundle.rglob('*') if p.is_file()}
assert names == set(manifest) | {'SHA256SUMS.txt'}
manifest['SHA256SUMS.txt'] = hashlib.sha256((bundle / 'SHA256SUMS.txt').read_bytes()).hexdigest()
report = {'status': 'PASS', 'archive': archive.name, 'archive_bytes': archive.stat().st_size, 'archive_sha256': expected, 'files': len(names), 'bundle': str(bundle), 'sha256': manifest}
(root / 'extraction-check.json').write_text(json.dumps(report, indent=2)+'\n', encoding='utf-8')
print(json.dumps({k:v for k,v in report.items() if k!='sha256'}, indent=2))
@@ -0,0 +1,575 @@
{
"status": "PASS",
"archive": "ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm.zip",
"archive_bytes": 34184534,
"archive_sha256": "18af75c7aea60bb0d5db5b1925d0d0815ecd08afc5bb258438851cfda809a6f2",
"files": 565,
"bundle": "C:\\ar-compact-01a0d717\\.local\\package39\\extracted\\ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm",
"sha256": {
"arcadia-server.exe": "6cebacc117e46fdab023c6402a5cebd6929fb55993ae8ff6b89cd1c6c1170237",
"arcadia-studio.exe": "d55ecad4b0f75c0c9c26625e5441bef9c9360890b65bc13236c6ebfaf03d631e",
"dependency-notices.json": "db8f66507719835a00f93a64bb9736e8b5e753b0dcca8b0fb1b36ca7b0bb91fd",
"deployment/arcadia-server.service": "1e6c6bb4065e25f8fe1d558a4ed0bbd4844782a4377c2482488db1e6f9cab01d",
"deployment/Dockerfile": "72c04aef4ec879955ed3bdf315987553d018ef46e4b8f350f0f6fe380c453eb7",
"deployment/Dockerfile.dockerignore": "6bb7d7496110b11a531cb8cd923864c6610850a8f1c4de8a797f54c7156f29e1",
"docs/api.html": "d06e32b3da838e519630a4151111bb40a383ccf294eb18443118a7498700a993",
"docs/api.rst": "d78f138d011dd0af28bd53a65d1d93381d031f680191779d4f3030a264eb2e74",
"docs/language.html": "77c14e548f5d2deea0c21a3a931347e1c87449f5be8503f6c51ceaef37be9a33",
"docs/language.rst": "7117a66e16b34cd6ce033532e984c7ca49b4611e296fb1c46c5f737fe9169ec6",
"docs/linux-packaging.md": "41a1bb1095801df37a1411341c5791d55ea8c365739314d599a60269ac4bc7a0",
"docs/openapi.json": "2ad69da52fb574435525990c906948ce54b1efd9247cdde8726969f382731790",
"docs/preview-human-checks.md": "50b56c328c22587e1187f6e614bc914210095cf635c301f335e82de11201b890",
"docs/research-v2-service.md": "4828df5bc679e7c71f49b4b85c9e7dacbec52f46d416fed48af70e08886d4e4e",
"docs/server.md": "4eb24937d424ba8b384e1734e02a6f8e963ae11d2715889c92b14b39ff2e4a48",
"docs/windows-packaging.md": "2d5ef8a9a8741e6e7f6df9db9fd9a839f806ea782a6f2063c3a98725ecfd36b6",
"documentation-check.json": "18a35f7022d6c180c42ad4fd7c7010e494bdff0d2ca395efac84ac545fe23627",
"examples/coastal-rescue.arcadia": "7e183ef9fd8154d3074303f1956cd0d9871c19e3ffc630d333b0bffbd6e30e17",
"examples/coastal-rescue.json": "242f0b031229a87af5c4ebe5d651defe59c191a6b7a69dc0a77f7e203411f195",
"examples/legacy-v1-coastal-rescue.arcadia": "4dbbbdf8bc7e6cb8e0a81dd678069dae585a9e5187c59bc3665e1fdab3f1a86a",
"examples/legacy-v1-coastal-rescue.oa.json": "fd0e9dedfb7adb922dc0575cc2b820d40344d639f1d8052bd70363aa59de9c24",
"examples/release1/evidence/repair-check.md": "f4db17bdca4ecbb1bf0caef01b2da5f1f7f798ab3152b648ce6dd8696a6a68e5",
"examples/release1/expected-diagnostics.json": "3700393d76b940cfc076bf52861bd7b1f43370f8eabce3594531bd0bf1fc851e",
"examples/release1/model.json": "ef0ef4006c3e09d5db6e1b621256b3f7d6642f3a7b09cc34607cfbcca830c727",
"examples/release1/model.rs": "ddc2d92b0d7699a4cadfbb35ae379f0a68d17f4f5d7df82b4b1afcf8d5425fbe",
"examples/release1/oa/activities/activity_oa_restore_work.arcadia": "21cbd7176af8fad68453306f2371ff03e127a994b8796cdf044567332de9686f",
"examples/release1/oa/behavior/process_oa_assessment_procedure.arcadia": "23cabc213c264a16c8595414db6acb09248c1cef9cd699f1b0316e5bb82c062a",
"examples/release1/oa/behavior/process_oa_field_procedure.arcadia": "9c52086759b7344b4809ceb8d372f65647b16fc24153a03b887faa378c37fe96",
"examples/release1/oa/behavior/process_oa_response.arcadia": "675798bd19e6b40d03e7076b0e59b110cdaaab7b6d16c47c3ac80fa960072e84",
"examples/release1/oa/behavior/scenario_oa_conversation.arcadia": "e217e5394a0771dc16206ed9437a231da27905d2f19be64b3f48281c0a32fb6b",
"examples/release1/oa/behavior/scenario_oa_full_scenario.arcadia": "c5a285f2d7c61bd2a136107f267fbf9cddb1cba3f63ff36a6f53f7ab98f33df5",
"examples/release1/oa/behavior/state_machine_oa_monitor.arcadia": "e7349e1e5336282eed3f5a103129db33d3ae4ed500788ee7129e8fc193ac57f6",
"examples/release1/oa/capabilities.arcadia": "26a3d9b615a3251ff010e0b47127f2d16bc2dcd45ed95967208b0076d725843a",
"examples/release1/oa/model.arcadia": "069a5eaaf525089935cc9d73ae7b916866ed78c6534705d75739650ecdea7ada",
"examples/release1/oa/participants.arcadia": "591a75a9ecad280302be4d4ae0171ad616377ed11ead5e233848ca2dc0a71819",
"examples/release1/oa/types.arcadia": "f02c391d584674c879f662a418abba3a220f1fee88471ca49322ac9873de628d",
"examples/release1/project.arcadia": "b2674e6bf77cfe0f6f0ba79c81d2e96d92f39c02ca2c6266d80e91723bfb9fda",
"examples/release1/README.md": "113cfb1cf38a98b51bff60442d86a35d636a3d80ef206bece7d53d0aae67f2d5",
"examples/release1/sa/behavior/process_sa_assessment_procedure.arcadia": "5c414fbdaabf9f9e402b0441fcacf1cacf4dbbc6c822162f028de35abf59be2d",
"examples/release1/sa/behavior/process_sa_field_procedure.arcadia": "ece846fdf93857e71a9ab0efcbdf64a5c41c71180f62afa3f79077fffb53b8f2",
"examples/release1/sa/behavior/process_sa_response.arcadia": "af7fdda280a917cf626f6e077162d481f9aaad280f8e81330c07d7fe384b1c52",
"examples/release1/sa/behavior/scenario_sa_conversation.arcadia": "01339699ec3ee409418d9bb232542f2bcd9419f1e67b183e77700c56bb9f71d2",
"examples/release1/sa/behavior/scenario_sa_full_scenario.arcadia": "5dce6b00a3a441162e8dee3e23d9ca5ab7bed4a24de669b1fba03664a3086dda",
"examples/release1/sa/behavior/state_machine_sa_monitor.arcadia": "17dbcfa5bbfd10fc8e23a325fc950ce853c219b4aa89c74f96278cbb3f34acc8",
"examples/release1/sa/capabilities.arcadia": "81706a0ca9d7db2aa0350888b9c00d853885185db2e6b90557d3d51a11c3d645",
"examples/release1/sa/functions/function_sa_audit_thresholds.arcadia": "27f798f78f36e72b38edab3725af2feb0eaa010fd52a60b5c6b6bef57ad3b686",
"examples/release1/sa/functions/function_sa_restore_work.arcadia": "357c6acb80f86b4e8d8f5c98f9f79d140f85fbe09f3406b41b7ac6eba464ec8c",
"examples/release1/sa/model.arcadia": "805b90262e27090ae7da81a51bcefa49c97fb0b3d3220b4a72323b4c4351c395",
"examples/release1/sa/participants.arcadia": "ca144d6a8f45fe720b5d18c7b59c7671444baa8c37e5ce9117bcba34b3a6aa84",
"examples/release1/sa/types.arcadia": "540ddbc2bcd0ccf45298e3ea5660637341aff63ff65fd87ac2f26469d1adc064",
"examples/release1/traceability.arcadia": "474bd28d955ff7af7722f6e9a23220d816879e3f46202e79a233544b9c593116",
"examples/release1/transition-before.json": "2253e6434f741687e22327c37d520ed15c043bc36025498650bc2366bb6a874d",
"examples/release1/transition-request.json": "43b9a22e5f95ad3bc0ebd2478ead0e14406854abc989ae94ded9367b013d812a",
"examples/release1/views/diagram_oa_allocation_view.arcadia": "c45d7467839219d894804c1ef2d22ff4d4a5a84a6a273c526bafd4c1facde624",
"examples/release1/views/diagram_sa_allocation_view.arcadia": "352a3ca745517592b5c9b1239b5770f62f0466a329a91fd03457ef2b53c06ed6",
"examples/release1/views/diagram_sa_capability_view.arcadia": "8f18fcccbcddcf3c1ea0ffe727b5b23b77fc148c5da4fde953abe681e4c20cd2",
"examples/release1/views/diagram_sa_service_view.arcadia": "4c72cb7593d9c4be8d2600b0656a7a3a6ce84c45bb2f3e58a4ee5ae3bf91913c",
"examples/release1/views/diagram_sa_state_view.arcadia": "dd1bea81ac7dc549a53f9b190be6f13052e00ede345bd180444cc31edb892230",
"examples/routing-workshop.arcadia": "dc6b75da72884d399132ed9e300fa5a26b73d802bb526c9af986ae9eeb0a646a",
"examples/routing-workshop.json": "8592714f75b271133f21ce017d87896b6cf91ea0e4837f1c2ed453e21be26b14",
"examples/source-project/oa.arcadia": "5f60642f211b2d352a951092faeeb7503d4fb7f6f7fca99e810347231101d31c",
"examples/source-project/project.arcadia": "787f6b67b179ffd221f7e5faf1a4edd74cfd116aa1e3b5e9ed9f22fbff612d8d",
"examples/source-project/sa/assurance.arcadia": "ab76044836e4dcd42a82d316846ec87da2108939aa2037640c5bca1d2ecdee0f",
"examples/source-project/sa/participants.arcadia": "1abad579812e77877b45a3d8e3c3e9da734be2d04a0254cc730a5257ebd2dc88",
"examples/source-project/sa/reporting.arcadia": "f0b7443f728912ca163de39b6055876b533a77feaa3219b2d40a2dd65712e058",
"examples/source-project/views.arcadia": "e29ad3f406a6a50d2f0ae2ca2a53ee3b0c18222cd4098704a46d4f48b64ea17f",
"examples/text-first/project.arcadia": "f441535c623e0cf20e1875eb2cd33b587ed00e078c37af39cb36d89bb5edc19a",
"examples/text-first/README.md": "4ec8250d5ed556f1d496e8c85137e973e802ecb11df1a36488411e8cdc5a9f9e",
"libunwind.dll": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4",
"LICENSE": "51009f2cb07e5d19645858b2fbc037a672a25f8cbf7999522942b86807ecf365",
"licenses/dependencies/arrayref-0.3.9/LICENSE": "1bc7e6f475b3ec99b7e2643411950ae2368c250dd4c5c325f80f9811362a94a1",
"licenses/dependencies/arrayvec-0.7.8/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/arrayvec-0.7.8/LICENSE-MIT": "4da95ec4ecb65b738d470b7d762894ad9c97da93e6cbfb18b570fc2c96f4b871",
"licenses/dependencies/ash-0.38.0+1.3.281/LICENSE-APACHE": "0e8a410375e1ee5c75c6198b08b534fec6e5edef766cf971bab4a404542636ab",
"licenses/dependencies/ash-0.38.0+1.3.281/LICENSE-MIT": "c733ccf8ab25df175a63d9d844677b98e3a5ab8f8de1c1616087e6868a7f948e",
"licenses/dependencies/atomic-waker-1.1.2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/atomic-waker-1.1.2/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/atomic-waker-1.1.2/LICENSE-THIRD-PARTY": "6226d0632e2e1a80c23597e964da9812ae193c535fe058154afb034e94167aa5",
"licenses/dependencies/autocfg-1.5.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/autocfg-1.5.1/LICENSE-MIT": "27995d58ad5c1145c1a8cd86244ce844886958a35eb2b78c6b772748669999ac",
"licenses/dependencies/axum-0.8.9/LICENSE": "6a13bc24a100a6812f053879ec51b126b103af7cda6dbf48c4188722da44da9f",
"licenses/dependencies/axum-core-0.5.6/LICENSE": "008c87afcd2e626eaf564093250bed06dd7efb5732113264bba3dda8f1c556a1",
"licenses/dependencies/base64-0.22.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/base64-0.22.1/LICENSE-MIT": "0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7",
"licenses/dependencies/bit-set-0.8.0/LICENSE-APACHE": "8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90",
"licenses/dependencies/bit-set-0.8.0/LICENSE-MIT": "f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7",
"licenses/dependencies/bit-vec-0.8.0/LICENSE-APACHE": "8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90",
"licenses/dependencies/bit-vec-0.8.0/LICENSE-MIT": "f51ac2c59a222f7476ce507ca879960e2b64ea64bb2786eefdbeb7b0b538d1b7",
"licenses/dependencies/bitflags-2.13.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/bitflags-2.13.1/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb",
"licenses/dependencies/bytemuck_derive-1.12.0/LICENSE-APACHE": "e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb",
"licenses/dependencies/bytemuck_derive-1.12.0/LICENSE-MIT": "9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2",
"licenses/dependencies/bytemuck_derive-1.12.0/LICENSE-ZLIB": "84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151",
"licenses/dependencies/bytemuck-1.25.2/LICENSE-APACHE": "e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb",
"licenses/dependencies/bytemuck-1.25.2/LICENSE-MIT": "9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2",
"licenses/dependencies/bytemuck-1.25.2/LICENSE-ZLIB": "84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151",
"licenses/dependencies/bytes-1.12.1/LICENSE": "45f522cacecb1023856e46df79ca625dfc550c94910078bd8aec6e02880b3d42",
"licenses/dependencies/cc-1.4.5/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/cc-1.4.5/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397",
"licenses/dependencies/cfg_aliases-0.2.2/LICENSE": "31b94860253d8ec7b4529f51901044d3b459d6292d996504a36b1bae3a36a812",
"licenses/dependencies/cfg-if-1.0.4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/cfg-if-1.0.4/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397",
"licenses/dependencies/chacha20-0.10.2/LICENSE-APACHE": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5",
"licenses/dependencies/chacha20-0.10.2/LICENSE-MIT": "b8c6939380a400f53e11923d50fcc4dd2fa1ba8339fd9d04cda38a0251b6c9b0",
"licenses/dependencies/clipboard-win-5.4.1/supplement/DoumanAsh-clipboard-win-3b27cf2bfd1a-LICENSE": "c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566",
"licenses/dependencies/codespan-reporting-0.12.0/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
"licenses/dependencies/core_maths-0.1.1/LICENSE": "9ebf8c4cc0b735ca13a766451f7b8097db3185975ceb2ba94b5abf439156a91f",
"licenses/dependencies/cosmic-text-0.15.0/fonts/FiraMono-LICENSE": "587dd9548304fd421cd254f7550bd101c1250ffc95417f2f7f92d55b44ecbe53",
"licenses/dependencies/cosmic-text-0.15.0/fonts/Inter-LICENSE": "3c8d13d97ea5eb62868833a377034c6be6a9833e768d32abec2a767a8101e4bc",
"licenses/dependencies/cosmic-text-0.15.0/fonts/NotoSans-LICENSE": "5ec4347054089def6904da61d04f0360968f9ef36634b1fe007a5b6ec2f7aa96",
"licenses/dependencies/cosmic-text-0.15.0/LICENSE-APACHE": "000b4962e6b27176a0ff89cce4be555b16472cafb5671eb2804a8fdac6854793",
"licenses/dependencies/cosmic-text-0.15.0/LICENSE-MIT": "95557bafe728379e206f3b6d4aceeaa054d271c71d722411a6222e2dec01138c",
"licenses/dependencies/cpufeatures-0.3.1/LICENSE-APACHE": "a9040321c3712d8fd0b09cf52b17445de04a23a10165049ae187cd39e5c86be5",
"licenses/dependencies/cpufeatures-0.3.1/LICENSE-MIT": "73b9dc2e79c7308998dd30296e073aefaefb944a68fb89aa412c23c0edcabcaa",
"licenses/dependencies/cryoglyph-0.1.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/cryoglyph-0.1.0/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/cryoglyph-0.1.0/LICENSE-ZLIB": "b3ec01ad0869c5c50937bc68780cd0bb44e235ad858dfa54a7a381dced9b115a",
"licenses/dependencies/cursor-icon-1.2.0/LICENSE-APACHE": "b59affd015e517ed998dd735757f9a3693643cd389609947ed54074234a10784",
"licenses/dependencies/cursor-icon-1.2.0/LICENSE-MIT": "c138726a726ac37bae09d0f8a48e74e8e3aa8a8952fcd10fae0fb224cc1c05f1",
"licenses/dependencies/cursor-icon-1.2.0/LICENSE-ZLIB": "01e533bd94e8f1c32b2c2531e2c398de7d35d7804add3d574b4e1fe581d53537",
"licenses/dependencies/displaydoc-0.2.7/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/displaydoc-0.2.7/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/document-features-0.2.12/LICENSE-APACHE": "074e6e32c86a4c0ef8b3ed25b721ca23aca83df277cd88106ef7177c354615ff",
"licenses/dependencies/document-features-0.2.12/LICENSE-MIT": "aa893340d14b9844625be6a50ac644169a01b52f0211cbf81b09e1874c8cd81d",
"licenses/dependencies/dpi-0.1.2/LICENSE": "6dc0e068dcf3a5bc8e054205b85b7720e1d49265bbc64bf515d2cf79197df69a",
"licenses/dependencies/dpi-0.1.2/LICENSE-LIBM-MIT": "9eff396a99bafd208e7126602420d6864f6066c08b84485a5b032320a9c01997",
"licenses/dependencies/equivalent-1.0.2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/equivalent-1.0.2/LICENSE-MIT": "7365cc8878a1d7ce155a58c4ca09c3d7a6be413efa5334a80ea842912b669349",
"licenses/dependencies/error-code-3.4.0/LICENSE": "c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566",
"licenses/dependencies/etagere-0.2.15/LICENSE-APACHE": "2d56a37f3d1e461f6b1539ab94e0e6bcc55443e376098aee85ac3b2890860290",
"licenses/dependencies/etagere-0.2.15/LICENSE-MIT": "9cfc9c9a4608501b5da3746948d2be2dd709ad51f6bcb0c1c685760ee3dde31f",
"licenses/dependencies/euclid-0.22.14/COPYRIGHT": "ec82b96487e9e778ee610c7ab245162464782cfa1f555c2299333f8dbe5c036a",
"licenses/dependencies/euclid-0.22.14/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/euclid-0.22.14/LICENSE-MIT": "62065228e42caebca7e7d7db1204cbb867033de5982ca4009928915e4095f3a3",
"licenses/dependencies/fastrand-2.5.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/fastrand-2.5.0/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/find-msvc-tools-0.1.12/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/find-msvc-tools-0.1.12/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397",
"licenses/dependencies/float_next_after-1.0.0/LICENSE": "0604d5817110dacc5c7d9b7de6b58b962794603d4823c357157bd5dec4198bc5",
"licenses/dependencies/foldhash-0.1.5/LICENSE": "b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744",
"licenses/dependencies/foldhash-0.2.0/LICENSE": "b1181a40b2a7b25cf66fd01481713bc1005df082c53ef73e851e55071b102744",
"licenses/dependencies/font-types-0.10.1/LICENSE-APACHE": "a14f7d685a79c0f4fb64aee305527a6d5fa55f2401f4e6d688d7b1301868e6c0",
"licenses/dependencies/font-types-0.10.1/LICENSE-MIT": "8a62fa27ffdccc3df19dce90f8bb3d572e27aecffc1a383d0c2601834dcec434",
"licenses/dependencies/font-types-0.12.4/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0",
"licenses/dependencies/font-types-0.12.4/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427",
"licenses/dependencies/fontdb-0.23.0/LICENSE": "59a1fdac3bd550043599bd159fd70f09a1108c2b09e3751d39b11e1ff8e51f00",
"licenses/dependencies/fontdb-0.23.0/tests/fonts/LICENSE.txt": "b84fdd2c3da5db56385cdbb639795e90aa3e035c53bc3591135f18df3331451f",
"licenses/dependencies/form_urlencoded-1.2.2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/form_urlencoded-1.2.2/LICENSE-MIT": "20c7855c364d57ea4c97889a5e8d98470a9952dade37bd9248b9a54431670e5e",
"licenses/dependencies/futures-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/futures-channel-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-channel-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/futures-core-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-core-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/futures-executor-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-executor-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/futures-io-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-io-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/futures-macro-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-macro-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/futures-sink-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-sink-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/futures-task-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-task-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/futures-util-0.3.34/LICENSE-APACHE": "275c491d6d1160553c32fd6127061d7f9606c3ea25abfad6ca3f6ed088785427",
"licenses/dependencies/futures-util-0.3.34/LICENSE-MIT": "6652c868f35dfe5e8ef636810a4e576b9d663f3a17fb0f5613ad73583e1b88fd",
"licenses/dependencies/getrandom-0.2.17/LICENSE-APACHE": "aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf",
"licenses/dependencies/getrandom-0.2.17/LICENSE-MIT": "42fa16951ce7f24b5a467a40e5b449a1d41e662f97ca779864f053f39e097737",
"licenses/dependencies/getrandom-0.4.3/LICENSE-APACHE": "aaff376532ea30a0cd5330b9502ad4a4c8bf769c539c87ffe78819d188a18ebf",
"licenses/dependencies/getrandom-0.4.3/LICENSE-MIT": "523a42c25d245dde9c015f882cec7f4555aad883382a6cf19b4b7d9b2cd5419b",
"licenses/dependencies/gl_generator-0.14.0/supplement/brendanzab-gl-rs-ea503e8d5fb6-LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/glam-0.25.0/LICENSE-APACHE": "be33ddb87a30fd321ab100e2d922cba512e50ec7a63d8e62e5bfc0798dd4b585",
"licenses/dependencies/glam-0.25.0/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/glow-0.16.0/LICENSE-APACHE": "b8601fc79bdf8d8088f0938bea861ffc63ed0eebbedf25f44ab2cf2e85bd7edc",
"licenses/dependencies/glow-0.16.0/LICENSE-MIT": "cdbd06e25b8c9c5d6019949d2de8123f56d29cee4166396423d2ba8c81700845",
"licenses/dependencies/glow-0.16.0/LICENSE-ZLIB": "636f644f1e149eb62961657c0e34953681eff64435653912e7bd19992666faa5",
"licenses/dependencies/glutin_wgl_sys-0.6.1/LICENSE": "a4445427440043e8ce6fadf7a43fb906cb0d7f6c573a560a9f142cf9f9bf8ff4",
"licenses/dependencies/gpu-alloc-0.6.2/supplement/zakarumych-gpu-alloc-61020bdcb4eb-COPYING": "12121dfa588677007848ab922a45a1e3248868f6ff063598fceb3141d7897314",
"licenses/dependencies/gpu-alloc-types-0.3.1/supplement/zakarumych-gpu-alloc-7ddd1fe576fc-COPYING": "12121dfa588677007848ab922a45a1e3248868f6ff063598fceb3141d7897314",
"licenses/dependencies/gpu-allocator-0.27.0/LICENSE-APACHE": "0178e21322b0e88aa3aeb3146f6a9611bc1f8df6d98bdfb34be28b9dd56a8107",
"licenses/dependencies/gpu-allocator-0.27.0/LICENSE-MIT": "ad41be6cc6538b29b9346648f41432b5e460bad6be073b5eeaa41320ea2921dc",
"licenses/dependencies/gpu-descriptor-0.3.2/supplement/zakarumych-gpu-descriptor-bb28b4e55b74-COPYING": "f72143f7e9d9fb8bd336b139fa22f825a299591c29f2f7338c5566347b137613",
"licenses/dependencies/gpu-descriptor-types-0.2.0/supplement/zakarumych-gpu-descriptor-9f2fb9546591-COPYING": "f72143f7e9d9fb8bd336b139fa22f825a299591c29f2f7338c5566347b137613",
"licenses/dependencies/guillotiere-0.6.2/LICENSE": "7e98bed7f100747aaae7d82ab94fbe6b76fd6b84b09a3aaa413bff8196d0fbb1",
"licenses/dependencies/half-2.7.1/LICENSE-APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/half-2.7.1/LICENSE-MIT": "508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1",
"licenses/dependencies/harfrust-0.3.2/LICENSE": "7d43d2ef1ce976c7bfb423c696ad9938ccfc31e9ec5be752265e43fe9d29f697",
"licenses/dependencies/harfrust-0.3.2/scripts/ms-use/COPYING": "9906940f61b1f0b533fa7d99baf55178b2808fbe113ea51dfbfad8572ccd5f2b",
"licenses/dependencies/hashbrown-0.15.5/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/hashbrown-0.15.5/LICENSE-MIT": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2",
"licenses/dependencies/hashbrown-0.16.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/hashbrown-0.16.1/LICENSE-MIT": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2",
"licenses/dependencies/hashbrown-0.17.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/hashbrown-0.17.1/LICENSE-MIT": "ff8f68cb076caf8cefe7a6430d4ac086ce6af2ca8ce2c4e5a2004d4552ef52a2",
"licenses/dependencies/hexf-parse-0.2.1/supplement/CC0-1.0.txt": "a2010f343487d3f7618affe54f789f5487602331c0a8d03f49e9a7c547cf0499",
"licenses/dependencies/hexf-parse-0.2.1/supplement/hexf-parse-0.2.1-Cargo.toml": "4a56dbdbc12054fd33b7a2634a45cbbe6fe12f5e6f4081002cbfa527f7220c70",
"licenses/dependencies/http-1.5.0/LICENSE-APACHE": "8bb1b50b0e5c9399ae33bd35fab2769010fa6c14e8860c729a52295d84896b7a",
"licenses/dependencies/http-1.5.0/LICENSE-MIT": "dc91f8200e4b2a1f9261035d4c18c33c246911a6c0f7b543d75347e61b249cff",
"licenses/dependencies/http-body-1.1.0/LICENSE": "248378d0a3383c173fb925f17141b88e71580b3ba17ddc6ac3d2a344683232ab",
"licenses/dependencies/http-body-util-0.1.5/LICENSE": "248378d0a3383c173fb925f17141b88e71580b3ba17ddc6ac3d2a344683232ab",
"licenses/dependencies/httparse-1.10.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/httparse-1.10.1/LICENSE-MIT": "391a5396cec6230bfabd4ef4eb2350eb895bc5efce377a2218f5702ed020d3e3",
"licenses/dependencies/httpdate-1.0.3/LICENSE-APACHE": "4d10fe5f3aa176b05b229a248866bad70b834c173f1252a814ff4748d8a13837",
"licenses/dependencies/httpdate-1.0.3/LICENSE-MIT": "934887691e05d69d7c86ad3f2c360980fa30c15b035e351f3c9865e99da4debc",
"licenses/dependencies/hyper-1.11.1/LICENSE": "2d01890414494742ba4a509fcec8efa40f6d8be22cbd72be7cff08d6fda4ec89",
"licenses/dependencies/hyper-rustls-0.27.9/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/hyper-rustls-0.27.9/LICENSE-ISC": "7cfafc877eccc46c0e346ccbaa5c51bb6b894d2b818e617d970211e232785ad4",
"licenses/dependencies/hyper-rustls-0.27.9/LICENSE-MIT": "709e3175b4212f7b13aa93971c9f62ff8c69ec45ad8c6532a7e0c41d7a7d6f8c",
"licenses/dependencies/hyper-util-0.1.20/LICENSE": "9e0a97848ea543aef745c98e84fde696a9a3e0735538f6daefdd3cb1942effc1",
"licenses/dependencies/iced_core-0.14.0/src/layout/DRUID_LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/iced_core-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_debug-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_futures-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_graphics-0.14.0/assets/FiraSans-Regular.ttf/Fira-OFL.txt": "99bfb7c1e845b79d1031fcf37e678178eb4a8c7cc74a3f218ddf7223083a6fcd",
"licenses/dependencies/iced_graphics-0.14.0/assets/FiraSans-Regular.ttf/FiraSans-Regular-NOTICE.txt": "1e0baaa389b1647d06322ac9ea37822f31995408200ae5a2805a6b9bea0810d1",
"licenses/dependencies/iced_graphics-0.14.0/assets/Iced-Icons.ttf/Iced-Icons-NOTICE.txt": "8175634e30f84ef72a24c4965fb0e87f45a3c1274cc0e79e0473af582cffd1c4",
"licenses/dependencies/iced_graphics-0.14.0/assets/Iced-Icons.ttf/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_graphics-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_program-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_renderer-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_runtime-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_tiny_skia-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_wgpu-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_widget-0.14.2/ARCADIA-PATCH.md": "d3acb98e5f423970b0bf4198373efddaea6f3a51aa282da571cf95c0f5690504",
"licenses/dependencies/iced_widget-0.14.2/LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced_winit-0.14.0/supplement/iced-rs-iced-3997291f318a-LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/iced-0.14.0/LICENSE": "fc9086ba4eba4b77e4c603906362742bc3715ffa2c7810c6074fe7780d3a0398",
"licenses/dependencies/icu_collections-2.3.0/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/icu_locale_core-2.3.0/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/icu_normalizer_data-2.3.0/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/icu_normalizer-2.3.0/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/icu_properties_data-2.3.0/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/icu_properties-2.3.0/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/icu_provider-2.3.1/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/idna_adapter-1.2.2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/idna_adapter-1.2.2/LICENSE-MIT": "8b43ce8accd61e9d370b5ca9e9c4f953279b5c239926c62315b40e24df51b726",
"licenses/dependencies/idna-1.1.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/idna-1.1.0/LICENSE-MIT": "b38f11f6096706e6de553dabe2a7ed142d59b6fa8c97e290c67496154745cdd5",
"licenses/dependencies/indexmap-2.14.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/indexmap-2.14.1/LICENSE-MIT": "ecc269ef87fd38a1d98e30bfac9ba964a9dbd9315c3770fed98d4d7cb5882055",
"licenses/dependencies/ipnet-2.12.1/LICENSE-APACHE": "87d9feb9238c6bd8e0024fc4733b06cff036f89f36d93b7df1c8a0549bbb7a5b",
"licenses/dependencies/ipnet-2.12.1/LICENSE-MIT": "47dc9ff29128ddfb4d6a0435383c9f89120bc374dbcc1dd00b933a0b28aa7865",
"licenses/dependencies/itoa-1.0.18/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/itoa-1.0.18/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/jobserver-0.1.35/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/jobserver-0.1.35/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397",
"licenses/dependencies/khronos_api-3.1.0/supplement/brendanzab-gl-rs-f150967b1c44-LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/khronos-egl-6.0.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/khronos-egl-6.0.0/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/kurbo-0.10.4/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/kurbo-0.10.4/LICENSE-MIT": "23c23145f6eac25c28cdcc03d38506a5712a9a8cffb5bd5301457f6173ea696e",
"licenses/dependencies/libc-0.2.189/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/libc-0.2.189/LICENSE-MIT": "123a331b5dbf04c30097fa43b8f858bc85df671fe776de498d01f3d6b7c1f69e",
"licenses/dependencies/libloading-0.8.9/LICENSE": "b29f8b01452350c20dd1af16ef83b598fea3053578ccc1c7a0ef40e57be2620f",
"licenses/dependencies/libm-0.2.16/LICENSE.txt": "3823dda7cf046602f4b4e77ec8e227863dc4736037cc85bb33d9f19febe16bb7",
"licenses/dependencies/lilt-0.8.2/LICENSE": "120b2f21f9b9f98ef4159de09f9a55a3afb6733dd1c0b1d9eeec7f136465b2cd",
"licenses/dependencies/linebender_resource_handle-0.1.1/LICENSE-APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/linebender_resource_handle-0.1.1/LICENSE-MIT": "508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1",
"licenses/dependencies/litemap-0.8.3/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/litrs-1.0.0/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/litrs-1.0.0/LICENSE-MIT": "7dc1552e88f49132cb358b1b962fc5e79fa42d70bcbb88c526d33e45b8e98036",
"licenses/dependencies/lock_api-0.4.14/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/lock_api-0.4.14/LICENSE-MIT": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5",
"licenses/dependencies/log-0.4.34/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/log-0.4.34/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb",
"licenses/dependencies/lru-0.16.4/LICENSE": "061dc50af2cd9340703daf61978af3200cf681b12ea67a323c33ba109a23a45e",
"licenses/dependencies/lru-slab-0.1.2/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/lru-slab-0.1.2/LICENSE-MIT": "1466d9b4f3a59bb8997fc8d5fa977ce3f6a24bb2358793c09bfdf007039f6486",
"licenses/dependencies/lru-slab-0.1.2/LICENSE-ZLIB": "09785877275ffada6c371b652a30e95c26b435126fc0fc62f1bc2d72171f80a4",
"licenses/dependencies/lyon_algorithms-1.0.21/supplement/nical-lyon-994526f2766c-LICENSE-APACHE": "216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06",
"licenses/dependencies/lyon_algorithms-1.0.21/supplement/nical-lyon-994526f2766c-LICENSE-MIT": "80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e",
"licenses/dependencies/lyon_geom-1.0.19/supplement/nical-lyon-56219c95c658-LICENSE-APACHE": "216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06",
"licenses/dependencies/lyon_geom-1.0.19/supplement/nical-lyon-56219c95c658-LICENSE-MIT": "80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e",
"licenses/dependencies/lyon_path-1.0.19/supplement/nical-lyon-56219c95c658-LICENSE-APACHE": "216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06",
"licenses/dependencies/lyon_path-1.0.19/supplement/nical-lyon-56219c95c658-LICENSE-MIT": "80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e",
"licenses/dependencies/lyon_tessellation-1.0.21/supplement/nical-lyon-994526f2766c-LICENSE-APACHE": "216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06",
"licenses/dependencies/lyon_tessellation-1.0.21/supplement/nical-lyon-994526f2766c-LICENSE-MIT": "80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e",
"licenses/dependencies/lyon-1.0.19/supplement/nical-lyon-56219c95c658-LICENSE-APACHE": "216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06",
"licenses/dependencies/lyon-1.0.19/supplement/nical-lyon-56219c95c658-LICENSE-MIT": "80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e",
"licenses/dependencies/matchit-0.8.4/LICENSE": "de701d0618d694feb1af90f02181a1763d9b0bdeb70a3a592781e529077dba65",
"licenses/dependencies/matchit-0.8.4/LICENSE.httprouter": "162ce11ad71338d0a0c6ebaf5c48af72c6ae237b468859d3656fe2d9ed3f3a85",
"licenses/dependencies/memchr-2.8.3/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f",
"licenses/dependencies/memchr-2.8.3/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f",
"licenses/dependencies/memchr-2.8.3/UNLICENSE": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c",
"licenses/dependencies/memmap2-0.9.11/LICENSE-APACHE": "04ea4849dba9dcae07113850c6f1b1a69052c625210639914eee352023f750ad",
"licenses/dependencies/memmap2-0.9.11/LICENSE-MIT": "0d25d03b5ab49576178ad0cae7a2648d12c17ad0452fe49c07e55e4b59aa5257",
"licenses/dependencies/mime-0.3.17/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/mime-0.3.17/LICENSE-MIT": "df9cfd06d8a44d9a671eadd39ffd97f166481da015a30f45dfd27886209c5922",
"licenses/dependencies/mio-1.2.3/LICENSE": "07919255c7e04793d8ea760d6c2ce32d19f9ff02bdbdde3ce90b1e1880929a9b",
"licenses/dependencies/naga-27.0.3/LICENSE.APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/naga-27.0.3/LICENSE.MIT": "dc0d97139e8205818c703741c7be7cb3b96888bd5917b8d6fc6133731e403c21",
"licenses/dependencies/num-bigint-0.5.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/num-bigint-0.5.1/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb",
"licenses/dependencies/num-integer-0.1.47/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/num-integer-0.1.47/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb",
"licenses/dependencies/num-traits-0.2.19/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/num-traits-0.2.19/LICENSE-MIT": "6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb",
"licenses/dependencies/oa-core-0.2.0/workspace-LICENSE": "51009f2cb07e5d19645858b2fbc037a672a25f8cbf7999522942b86807ecf365",
"licenses/dependencies/oa-desktop-0.2.0/workspace-LICENSE": "51009f2cb07e5d19645858b2fbc037a672a25f8cbf7999522942b86807ecf365",
"licenses/dependencies/oa-server-0.2.0/workspace-LICENSE": "51009f2cb07e5d19645858b2fbc037a672a25f8cbf7999522942b86807ecf365",
"licenses/dependencies/once_cell-1.21.4/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/once_cell-1.21.4/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/ordered-float-5.5.0/LICENSE-MIT": "f7715d38a3fa1b4ac97c5729740752505a39cb92ee83ab5b102aeb5eaa7cdea4",
"licenses/dependencies/parking_lot_core-0.9.12/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/parking_lot_core-0.9.12/LICENSE-MIT": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5",
"licenses/dependencies/parking_lot-0.12.5/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/parking_lot-0.12.5/LICENSE-MIT": "c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5",
"licenses/dependencies/percent-encoding-2.3.2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/percent-encoding-2.3.2/LICENSE-MIT": "b38f11f6096706e6de553dabe2a7ed142d59b6fa8c97e290c67496154745cdd5",
"licenses/dependencies/pin-project-lite-0.2.17/LICENSE-APACHE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594",
"licenses/dependencies/pin-project-lite-0.2.17/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/pkg-config-0.3.34/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/pkg-config-0.3.34/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397",
"licenses/dependencies/potential_utf-0.1.6/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/presser-0.3.1/LICENSE-APACHE": "8173d5c29b4f956d532781d2b86e4e30f83e6b7878dce18c919451d6ba707c90",
"licenses/dependencies/presser-0.3.1/LICENSE-MIT": "090a294a492ab2f41388252312a65cf2f0e423330b721a68c6665ac64766753b",
"licenses/dependencies/proc-macro2-1.0.107/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/proc-macro2-1.0.107/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/profiling-1.0.18/supplement/aclysma-profiling-8271551172eb-LICENSE-APACHE": "10d30a673cd5e9349bdc02aeb48f14b3386d27d0da32df8f0a555d4aa16aa551",
"licenses/dependencies/profiling-1.0.18/supplement/aclysma-profiling-8271551172eb-LICENSE-MIT": "c8167fdeeed46d3f244d3f85c5bf998ce889343691c32be2c61a8bc4b5c08333",
"licenses/dependencies/quinn-0.11.11/LICENSE-APACHE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
"licenses/dependencies/quinn-0.11.11/LICENSE-MIT": "4b2d0aca6789fa39e03d6738e869ea0988cceba210ca34ebb59c15c463e93a04",
"licenses/dependencies/quinn-proto-0.11.17/LICENSE-APACHE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
"licenses/dependencies/quinn-proto-0.11.17/LICENSE-MIT": "4b2d0aca6789fa39e03d6738e869ea0988cceba210ca34ebb59c15c463e93a04",
"licenses/dependencies/quinn-udp-0.5.15/LICENSE-APACHE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
"licenses/dependencies/quinn-udp-0.5.15/LICENSE-MIT": "4b2d0aca6789fa39e03d6738e869ea0988cceba210ca34ebb59c15c463e93a04",
"licenses/dependencies/quote-1.0.47/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/quote-1.0.47/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/rand_core-0.10.1/COPYRIGHT": "92b81db30f7ab6d693e0af5661a40d273ca1947e891123ed823924192c10cbc7",
"licenses/dependencies/rand_core-0.10.1/LICENSE-APACHE": "6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51",
"licenses/dependencies/rand_core-0.10.1/LICENSE-MIT": "8b6e9feec03e7c9a5facb26855cecd31662bf989b636bcfe79521bdf8ac863f0",
"licenses/dependencies/rand_pcg-0.10.2/COPYRIGHT": "90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5",
"licenses/dependencies/rand_pcg-0.10.2/LICENSE-APACHE": "6df43f6f4b5d4587f3d8d71e45532c688fd168afa5fe89d571cb32fa09c4ef51",
"licenses/dependencies/rand_pcg-0.10.2/LICENSE-MIT": "2234e3cefee876aeb686ad89e978bdb07bf118a1186ab1cf161bcdf69d4b4f57",
"licenses/dependencies/rand-0.10.2/COPYRIGHT": "90eb64f0279b0d9432accfa6023ff803bc4965212383697eee27a0f426d5f8d5",
"licenses/dependencies/rand-0.10.2/LICENSE-APACHE": "35242e7a83f69875e6edeff02291e688c97caafe2f8902e4e19b49d3e78b4cab",
"licenses/dependencies/rand-0.10.2/LICENSE-MIT": "209fbbe0ad52d9235e37badf9cadfe4dbdc87203179c0899e738b39ade42177b",
"licenses/dependencies/range-alloc-0.1.5/LICENSE.APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/range-alloc-0.1.5/LICENSE.MIT": "ba00052102ed87948bb8822d6e1c496260c83042887b22bb5caad922ec025a60",
"licenses/dependencies/rangemap-1.8.0/LICENSE-APACHE": "1c7745cc26c7294bc9dbc883bcd50700b99a8c0f4859bfce84ec555098ce1ab5",
"licenses/dependencies/rangemap-1.8.0/LICENSE-MIT": "29a4c3823ee29ee5a97c145574a1bac20629fd5e6708be1df0d25bab563e093c",
"licenses/dependencies/raw-window-handle-0.6.2/LICENSE-APACHE.md": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594",
"licenses/dependencies/raw-window-handle-0.6.2/LICENSE-MIT.md": "9c5a80639a57c1c945570e7ebbca0706305849ce3c098021325cca9db2f7acc4",
"licenses/dependencies/raw-window-handle-0.6.2/LICENSE-ZLIB.md": "ae895c8576e682f310fb70d93d9f99b1dd82872094c54a8f6cdfe2ca5c2d6ecb",
"licenses/dependencies/read-fonts-0.35.0/LICENSE-APACHE": "a14f7d685a79c0f4fb64aee305527a6d5fa55f2401f4e6d688d7b1301868e6c0",
"licenses/dependencies/read-fonts-0.35.0/LICENSE-MIT": "8a62fa27ffdccc3df19dce90f8bb3d572e27aecffc1a383d0c2601834dcec434",
"licenses/dependencies/read-fonts-0.41.0/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0",
"licenses/dependencies/read-fonts-0.41.0/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427",
"licenses/dependencies/renderdoc-sys-1.1.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/renderdoc-sys-1.1.0/LICENSE-MIT": "24b2f2eff5a7622e87162823e260f00c6dfbce0e26d585d3d28c785f1edc80ed",
"licenses/dependencies/reqwest-0.12.28/LICENSE-APACHE": "751963a8b88c0e3a9f27e98933079fbcf09b9998b2c13ac49c2e4d58444520d6",
"licenses/dependencies/reqwest-0.12.28/LICENSE-MIT": "47d4e1803702728e03d30f8a848ae2249ba274bbd9f8443e803f7924d83cd371",
"licenses/dependencies/rfd-0.15.4/LICENSE": "65fc947f4bac882a2cd104ef73c246b1b037059df9f2b3a58b7f77f0b9e56071",
"licenses/dependencies/ring-0.17.14/LICENSE": "b3d734001a94efff3579978d953391aa7115f877657d25eb54037a43875d078a",
"licenses/dependencies/ring-0.17.14/LICENSE-BoringSSL": "005fc765ddc5115da796cca915baa9557abae13ff35e0a47c47affc56f6c414d",
"licenses/dependencies/ring-0.17.14/LICENSE-other-bits": "f025ccfb7dfb6bdfedc75ca0f67acc69e6fb4998143d834f7c2f38a29989680f",
"licenses/dependencies/ring-0.17.14/src/polyfill/once_cell/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/ring-0.17.14/src/polyfill/once_cell/LICENSE-MIT": "6ee2ed6c77710de911761acd5fc1ad1da00f476beb1a7ef27e78c2d1858deafc",
"licenses/dependencies/ring-0.17.14/third_party/fiat/LICENSE": "9eacbcb81be660840c714a560a9d65ba07913db98dd4baf969f78dd499fdd60f",
"licenses/dependencies/rustc-hash-1.1.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/rustc-hash-1.1.0/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/rustc-hash-2.1.3/LICENSE-APACHE": "95bd3988beee069fa2848f648dab43cc6e0b2add2ad6bcb17360caf749802bcc",
"licenses/dependencies/rustc-hash-2.1.3/LICENSE-MIT": "30fefc3a7d6a0041541858293bcbea2dde4caa4c0a5802f996a7f7e8c0085652",
"licenses/dependencies/rustls-0.23.43/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/rustls-0.23.43/LICENSE-ISC": "7cfafc877eccc46c0e346ccbaa5c51bb6b894d2b818e617d970211e232785ad4",
"licenses/dependencies/rustls-0.23.43/LICENSE-MIT": "709e3175b4212f7b13aa93971c9f62ff8c69ec45ad8c6532a7e0c41d7a7d6f8c",
"licenses/dependencies/rustls-pki-types-1.15.1/LICENSE-APACHE": "45fd05c4865e7c350b98ad7ac50e1b15462d49af4a91e9b0c9dd933dc9a69742",
"licenses/dependencies/rustls-pki-types-1.15.1/LICENSE-MIT": "9117d922e667125508dde62b02c1f57ed22f5ad21eb536aa2e2d99e1c796e639",
"licenses/dependencies/rustls-webpki-0.103.15/LICENSE": "5b698ca13897be3afdb7174256fa1574f8c6892b8bea1a66dd6469d3fe27885a",
"licenses/dependencies/ryu-1.0.23/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/ryu-1.0.23/LICENSE-BOOST": "c9bff75738922193e67fa726fa225535870d2aa1059f91452c411736284ad566",
"licenses/dependencies/same-file-1.0.6/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f",
"licenses/dependencies/same-file-1.0.6/LICENSE-MIT": "cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14",
"licenses/dependencies/same-file-1.0.6/UNLICENSE": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c",
"licenses/dependencies/scopeguard-1.2.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/scopeguard-1.2.0/LICENSE-MIT": "fb77f0a9c53e473abe5103c8632ef9f0f2874d4fb3f17cb2d8c661aab9cee9d7",
"licenses/dependencies/self_cell-1.3.0/LICENSE-APACHE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
"licenses/dependencies/self_cell-1.3.0/LICENSE-GPLv2": "f9c375a1be4a41f7b70301dd83c91cb89e41567478859b77eef375a52d782505",
"licenses/dependencies/serde_core-1.0.229/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/serde_core-1.0.229/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/serde_derive-1.0.229/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/serde_derive-1.0.229/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/serde_json-1.0.151/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/serde_json-1.0.151/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/serde_path_to_error-0.1.20/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/serde_path_to_error-0.1.20/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/serde_urlencoded-0.7.1/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/serde_urlencoded-0.7.1/LICENSE-MIT": "b9eb266294324f672cbe945fe8f2e32f85024f0d61a1a7d14382cdde0ac44769",
"licenses/dependencies/serde-1.0.229/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/serde-1.0.229/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/sha1_smol-1.0.1/LICENSE": "282a21b0710441c364025461a447362daf18f5798a7b72ae2f801a2aa536da51",
"licenses/dependencies/shlex-2.0.1/LICENSE-APACHE": "553fffcd9b1cb158bc3e9edc35da85ca5c3b3d7d2e61c883ebcfa8a65814b583",
"licenses/dependencies/shlex-2.0.1/LICENSE-MIT": "4455bf75a91154108304cb283e0fea9948c14f13e20d60887cf2552449dea3b1",
"licenses/dependencies/skrifa-0.37.0/LICENSE-APACHE": "a14f7d685a79c0f4fb64aee305527a6d5fa55f2401f4e6d688d7b1301868e6c0",
"licenses/dependencies/skrifa-0.37.0/LICENSE-MIT": "8a62fa27ffdccc3df19dce90f8bb3d572e27aecffc1a383d0c2601834dcec434",
"licenses/dependencies/skrifa-0.44.0/LICENSE-APACHE": "eba684204073ed715c4abb48129acdfd2bff8ae48339e6a7da3b341d3027b7f0",
"licenses/dependencies/skrifa-0.44.0/LICENSE-MIT": "7b4c9a3946dfcea7967582760e963e27799e225dcf20565c8ef55324bb017427",
"licenses/dependencies/slab-0.4.12/LICENSE": "8ce0830173fdac609dfb4ea603fdc002c2f4af0dc9b1a005653f5da9cf534b18",
"licenses/dependencies/slotmap-1.1.1/LICENSE": "6cec81441b2ab2b5090954c1fb5d0bcca81b86487b5849061fa8952d20ef67d4",
"licenses/dependencies/smallvec-1.16.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/smallvec-1.16.0/LICENSE-MIT": "0b28172679e0009b655da42797c03fd163a3379d5cfa67ba1f1655e974a2a1a9",
"licenses/dependencies/smol_str-0.2.2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/smol_str-0.2.2/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/socket2-0.6.5/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/socket2-0.6.5/LICENSE-MIT": "378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397",
"licenses/dependencies/softbuffer-0.4.8/LICENSE-APACHE": "a4445427440043e8ce6fadf7a43fb906cb0d7f6c573a560a9f142cf9f9bf8ff4",
"licenses/dependencies/softbuffer-0.4.8/LICENSE-MIT": "0463bfa47d6d763d8c824333efb0e6d6df3946c13c3b077c78ed9f23ca50d0f2",
"licenses/dependencies/spirv-0.3.0+sdk-1.3.268.0/supplement/gfx-rs-rspirv-e91a5debc10c-LICENSE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/stable_deref_trait-1.2.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/stable_deref_trait-1.2.1/LICENSE-MIT": "5e05b024f653a5ce199e77cbbbd42fb5553562ec714b819421ed0c3e552a75d7",
"licenses/dependencies/static_assertions-1.1.0/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/static_assertions-1.1.0/LICENSE-MIT": "ea084a2373ebc1f0902c09266e7bf25a05ab3814c1805bb017ffa7308f90c061",
"licenses/dependencies/strict-num-0.1.1/LICENSE": "3085885678bb161f6c472bbd331f66a2550b555eb76128f7f8247779128199c0",
"licenses/dependencies/subtle-2.6.1/LICENSE": "d1fc1bc0d155df60b2e7705b6b2ae02a05c96f948e1cec6e2fb86360b09f346b",
"licenses/dependencies/svg_fmt-0.4.5/supplement/nical-rust_debug-93f414cd572b-LICENSE": "7e98bed7f100747aaae7d82ab94fbe6b76fd6b84b09a3aaa413bff8196d0fbb1",
"licenses/dependencies/swash-0.2.10/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/swash-0.2.10/LICENSE-MIT": "122dadfc2eb4de0f4bafcd9eb4e7502cfe338a7965ebd1d18708cc5050f43f76",
"licenses/dependencies/syn-2.0.119/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/syn-2.0.119/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/syn-3.0.5/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/syn-3.0.5/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/sync_wrapper-1.0.2/LICENSE": "0d542e0c8804e39aa7f37eb00da5a762149dc682d7829451287e11b938e94594",
"licenses/dependencies/synstructure-0.13.2/LICENSE": "219920e865eee70b7dcfc948a86b099e7f4fe2de01bcca2ca9a20c0a033f2b59",
"licenses/dependencies/sys-locale-0.3.2/LICENSE-APACHE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
"licenses/dependencies/sys-locale-0.3.2/LICENSE-MIT": "5cc390ab5e5e6507f467368cbab1297552d64bfd863f4b496c7928508bcfefd8",
"licenses/dependencies/tempfile-3.27.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/tempfile-3.27.0/LICENSE-MIT": "8b427f5bc501764575e52ba4f9d95673cf8f6d80a86d0d06599852e1a9a20a36",
"licenses/dependencies/termcolor-1.4.1/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f",
"licenses/dependencies/termcolor-1.4.1/LICENSE-MIT": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f",
"licenses/dependencies/termcolor-1.4.1/UNLICENSE": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c",
"licenses/dependencies/thiserror-1.0.69/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/thiserror-1.0.69/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/thiserror-2.0.20/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/thiserror-2.0.20/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/thiserror-impl-1.0.69/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/thiserror-impl-1.0.69/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/thiserror-impl-2.0.20/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/thiserror-impl-2.0.20/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/tiny-skia-0.11.4/LICENSE": "6d41e05f1d54fe726997cb0334ec539f1dff50600b1d5d90462ec2b392770ced",
"licenses/dependencies/tiny-skia-path-0.11.4/LICENSE": "6d41e05f1d54fe726997cb0334ec539f1dff50600b1d5d90462ec2b392770ced",
"licenses/dependencies/tinystr-0.8.4/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/tinyvec_macros-0.1.1/LICENSE-APACHE.md": "4f44572785f35152c1fd2eadf565b7e079c0f300b4324f0af653419f9d76b735",
"licenses/dependencies/tinyvec_macros-0.1.1/LICENSE-MIT.md": "1dd8eca0f83669e75fa119e34fb9e1be9d16e3e9b6368962b8019db6e8ae5f7b",
"licenses/dependencies/tinyvec_macros-0.1.1/LICENSE-ZLIB.md": "41ace205715d9f19a3214218cc1c01d57c533e02cd0fef7c8e51a49a7fce5ac5",
"licenses/dependencies/tinyvec-1.13.2/LICENSE-APACHE.md": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/tinyvec-1.13.2/LICENSE-MIT.md": "fd80a26fbb3f644af1fa994134446702932968519797227e07a1368dea80f0bc",
"licenses/dependencies/tinyvec-1.13.2/LICENSE-ZLIB.md": "84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151",
"licenses/dependencies/tokio-1.53.1/LICENSE": "253cd04c6714889df2d32f3f64d669179a1c95c76ac43c40882c52eb06bc3552",
"licenses/dependencies/tokio-macros-2.7.2/LICENSE": "0b83dc40cba89b9922bb84b0a9c7d2768ce37c1d7e138b7424fd4549915778c9",
"licenses/dependencies/tokio-rustls-0.26.5/LICENSE-APACHE": "cc117d90b498b32b11a886f279b359da16a73c3b01efbb2f5cc004b20262334e",
"licenses/dependencies/tokio-rustls-0.26.5/LICENSE-MIT": "e20fa2b8e0a2565f24a792b94b4bf4b6c2b9d36f781d8a9516e218a036e6677a",
"licenses/dependencies/tower-0.5.3/LICENSE": "4249c8e6c5ebb85f97c77e6457c6fafc1066406eb8f1ef61e796fbdc5ff18482",
"licenses/dependencies/tower-http-0.6.11/LICENSE": "5049cf464977eff4b4fcfa7988d84e74116956a3eb9d5f1d451b3f828f945233",
"licenses/dependencies/tower-layer-0.3.3/LICENSE": "4249c8e6c5ebb85f97c77e6457c6fafc1066406eb8f1ef61e796fbdc5ff18482",
"licenses/dependencies/tower-service-0.3.3/LICENSE": "4249c8e6c5ebb85f97c77e6457c6fafc1066406eb8f1ef61e796fbdc5ff18482",
"licenses/dependencies/tracing-0.1.44/LICENSE": "898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1",
"licenses/dependencies/tracing-attributes-0.1.31/LICENSE": "898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1",
"licenses/dependencies/tracing-core-0.1.36/LICENSE": "898b1ae9821e98daf8964c8d6c7f61641f5f5aa78ad500020771c0939ee0dea1",
"licenses/dependencies/tracing-core-0.1.36/src/spin/LICENSE": "58545fed1565e42d687aecec6897d35c6d37ccb71479a137c0deb2203e125c79",
"licenses/dependencies/try-lock-0.2.5/LICENSE": "c816a0749cdc6bf062a5111c159723de51b2bfac66a1dac2655abd9e6b1583eb",
"licenses/dependencies/ttf-parser-0.25.1/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/ttf-parser-0.25.1/LICENSE-MIT": "f3c9fe731c701ed77fd5fbaac573872af04dc5869385eb9bd5e3c73c42713814",
"licenses/dependencies/ttf-parser-0.25.1/tests/fonts/colr_1_LICENSE": "58d1e17ffe5109a7ae296caafcadfdbe6a7d176f0bc4ab01e12a689b0499d8bd",
"licenses/dependencies/unicode-bidi-0.3.18/COPYRIGHT": "edb20b474f6cbd4f4db066b54a9e0f687d0009d309412a63431189b59b8e2a07",
"licenses/dependencies/unicode-bidi-0.3.18/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/unicode-bidi-0.3.18/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0",
"licenses/dependencies/unicode-ident-1.0.24/LICENSE-APACHE": "62c7a1e35f56406896d7aa7ca52d0cc0d272ac022b5d2796e7d6905db8a3636a",
"licenses/dependencies/unicode-ident-1.0.24/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/dependencies/unicode-ident-1.0.24/LICENSE-UNICODE": "f7db81051789b729fea528a63ec4c938fdcb93d9d61d97dc8cc2e9df6d47f2a1",
"licenses/dependencies/unicode-linebreak-0.1.5/LICENSE": "c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4",
"licenses/dependencies/unicode-script-0.5.8/LICENSE-APACHE": "7cbb56d1b5d83d735056b363ab20958524120afafc4ad8206e6be98cdec5d737",
"licenses/dependencies/unicode-script-0.5.8/LICENSE-MIT": "7ad3ea8ca3caf894db98c3f31d5d4949173ffa93c883ef19a9e529ad48960f8c",
"licenses/dependencies/unicode-segmentation-1.13.3/COPYRIGHT": "23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d",
"licenses/dependencies/unicode-segmentation-1.13.3/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/unicode-segmentation-1.13.3/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0",
"licenses/dependencies/unicode-width-0.2.2/COPYRIGHT": "23860c2a7b5d96b21569afedf033469bab9fe14a1b24a35068b8641c578ce24d",
"licenses/dependencies/unicode-width-0.2.2/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/unicode-width-0.2.2/LICENSE-MIT": "7b63ecd5f1902af1b63729947373683c32745c16a10e8e6292e2e2dcd7e90ae0",
"licenses/dependencies/untrusted-0.9.0/LICENSE.txt": "7abd9b6960dcf7d4d0a48606a5b71bfe37d472db68d70637f3a58a56785f1621",
"licenses/dependencies/url-2.5.8/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/url-2.5.8/LICENSE-MIT": "b38f11f6096706e6de553dabe2a7ed142d59b6fa8c97e290c67496154745cdd5",
"licenses/dependencies/utf8_iter-1.0.4/COPYRIGHT": "c30152c94a6d75e021adbc52b3a52470366a46edb917e17deae3259251af244c",
"licenses/dependencies/utf8_iter-1.0.4/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/utf8_iter-1.0.4/LICENSE-MIT": "3fa4ca83dcc9237839b1bdeb2e6d16bdfb5ec0c5ce42b24694d8bbf0dcbef72c",
"licenses/dependencies/uuid-1.26.0/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/uuid-1.26.0/LICENSE-MIT": "436bc5a105d8e57dcd8778730f3754f7bf39c14d2f530e4cde4bd2d17a83ec3d",
"licenses/dependencies/version_check-0.9.5/LICENSE-APACHE": "a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2",
"licenses/dependencies/version_check-0.9.5/LICENSE-MIT": "b7e650f3fce5c53249d1cdc608b54df156a97edd636cf9d23498d0cfe7aec63e",
"licenses/dependencies/want-0.3.1/LICENSE": "a65f5d0a945d267751344c95665945b90c030ea107faf5c85d518929886187da",
"licenses/dependencies/web-time-1.1.0/LICENSE-APACHE": "54a744942eb3fa63a74d8d359989ed6962c04f3f10ff20cfa08ff8aa7efbe4e6",
"licenses/dependencies/web-time-1.1.0/LICENSE-MIT": "f428305bbf2e70fbd807af0f3c7ac1b7acfb95311640f451e9d2e48db6d7e96b",
"licenses/dependencies/webpki-roots-1.0.9/LICENSE": "e271993808fec50ab29350b39539cdec611a9103f827e0aa26d61da70e2d33f8",
"licenses/dependencies/wgpu-27.0.1/LICENSE.APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/wgpu-27.0.1/LICENSE.MIT": "dc0d97139e8205818c703741c7be7cb3b96888bd5917b8d6fc6133731e403c21",
"licenses/dependencies/wgpu-core-27.0.3/LICENSE.APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/wgpu-core-27.0.3/LICENSE.MIT": "dc0d97139e8205818c703741c7be7cb3b96888bd5917b8d6fc6133731e403c21",
"licenses/dependencies/wgpu-core-deps-windows-linux-android-27.0.0/LICENSE.APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/wgpu-core-deps-windows-linux-android-27.0.0/LICENSE.MIT": "dc0d97139e8205818c703741c7be7cb3b96888bd5917b8d6fc6133731e403c21",
"licenses/dependencies/wgpu-hal-27.0.4/LICENSE.APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/wgpu-hal-27.0.4/LICENSE.MIT": "dc0d97139e8205818c703741c7be7cb3b96888bd5917b8d6fc6133731e403c21",
"licenses/dependencies/wgpu-types-27.0.1/LICENSE.APACHE": "a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9",
"licenses/dependencies/wgpu-types-27.0.1/LICENSE.MIT": "dc0d97139e8205818c703741c7be7cb3b96888bd5917b8d6fc6133731e403c21",
"licenses/dependencies/winapi-util-0.1.11/COPYING": "01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f",
"licenses/dependencies/winapi-util-0.1.11/LICENSE-MIT": "cb3c929a05e6cbc9de9ab06a4c57eeb60ca8c724bef6c138c87d3a577e27aa14",
"licenses/dependencies/winapi-util-0.1.11/UNLICENSE": "7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c",
"licenses/dependencies/window_clipboard-0.5.1/LICENSE": "7faec985f3e9b1c4a1df646bfedc304ba67e409447f7ab7f4f591f68708d85dc",
"licenses/dependencies/windows_x86_64_gnullvm-0.52.6/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows_x86_64_gnullvm-0.52.6/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-0.58.0/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-0.58.0/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-core-0.58.0/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-core-0.58.0/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-implement-0.58.0/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-implement-0.58.0/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-interface-0.58.0/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-interface-0.58.0/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-link-0.2.1/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-link-0.2.1/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-result-0.2.0/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-result-0.2.0/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-strings-0.1.0/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-strings-0.1.0/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-sys-0.52.0/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-sys-0.52.0/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-sys-0.59.0/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-sys-0.59.0/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-sys-0.61.2/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-sys-0.61.2/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/windows-targets-0.52.6/license-apache-2.0": "c16f8dcf1a368b83be78d826ea23de4079fe1b4469a0ab9ee20563f37ff3d44b",
"licenses/dependencies/windows-targets-0.52.6/license-mit": "c2cfccb812fe482101a8f04597dfc5a9991a6b2748266c47ac91b6a5aae15383",
"licenses/dependencies/winit-0.30.13/LICENSE": "6dc0e068dcf3a5bc8e054205b85b7720e1d49265bbc64bf515d2cf79197df69a",
"licenses/dependencies/writeable-0.6.4/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/xml-rs-0.8.29/LICENSE": "0dc18d924dc0a5f41172a393012843a5eaaef338e795b3645da9cc3b6068220b",
"licenses/dependencies/yazi-0.2.1/LICENSE-APACHE": "7cfd738c53d61c79f07e348f622bf7707c9084237054d37fbe07788a75f5881c",
"licenses/dependencies/yazi-0.2.1/LICENSE-MIT": "df62e3ef3f207c91073fedd78b4156a278681d0dd3f942379242c62c6a71a3ec",
"licenses/dependencies/yoke-0.8.3/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/yoke-derive-0.8.2/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/zeno-0.3.3/LICENSE-APACHE": "7cfd738c53d61c79f07e348f622bf7707c9084237054d37fbe07788a75f5881c",
"licenses/dependencies/zeno-0.3.3/LICENSE-MIT": "34dff8202d63e6df022f4f0ce88dab24f254c35ee0d5958b74dcf74d9eae21cc",
"licenses/dependencies/zerocopy-0.8.56/LICENSE-APACHE": "9d185ac6703c4b0453974c0d85e9eee43e6941009296bb1f5eb0b54e2329e9f3",
"licenses/dependencies/zerocopy-0.8.56/LICENSE-BSD": "83c1763356e822adde0a2cae748d938a73fdc263849ccff6b27776dff213bd32",
"licenses/dependencies/zerocopy-0.8.56/LICENSE-MIT": "1a2f5c12ddc934d58956aa5dbdd3255fe55fd957633ab7d0d39e4f0daa73f7df",
"licenses/dependencies/zerocopy-derive-0.8.56/LICENSE-APACHE": "9d185ac6703c4b0453974c0d85e9eee43e6941009296bb1f5eb0b54e2329e9f3",
"licenses/dependencies/zerocopy-derive-0.8.56/LICENSE-BSD": "83c1763356e822adde0a2cae748d938a73fdc263849ccff6b27776dff213bd32",
"licenses/dependencies/zerocopy-derive-0.8.56/LICENSE-MIT": "1a2f5c12ddc934d58956aa5dbdd3255fe55fd957633ab7d0d39e4f0daa73f7df",
"licenses/dependencies/zerofrom-0.1.8/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/zerofrom-derive-0.1.7/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/zeroize-1.9.0/LICENSE-APACHE": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30",
"licenses/dependencies/zeroize-1.9.0/LICENSE-MIT": "8c7516d4b27b1e495be5e38b612298b63de48d05f49cdac94f70f3cd70f8864b",
"licenses/dependencies/zerotrie-0.2.5/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/zerovec-0.11.8/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/zerovec-derive-0.11.6/LICENSE": "f367c1b8e1aa262435251e442901da4607b4650e0e63a026f5044473ecfb90f2",
"licenses/dependencies/zmij-1.0.23/LICENSE-MIT": "23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3",
"licenses/llvm-libunwind/llvm-ea7d852a70e8-libunwind-LICENSE.TXT": "b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d",
"licenses/llvm-mingw-LICENSE.TXT": "8d85c1057d742e597985c7d4e6320b015a9139385cff4cbae06ffc0ebe89afee",
"licenses/rust/COPYING.RUNTIME": "28e85c5aa4af9b4f1dfe6b4817aa3eefb3eaaee7fd735045016f29ccf50276a1",
"licenses/rust/COPYING3": "8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903",
"licenses/rust/COPYRIGHT-library.html": "68129500b616d5838629e68f55ff3aed5e096dacf60ce9eb41bbe599a563afa6",
"licenses/rust/COPYRIGHT.html": "5b0c93fc4e6d4b072eaa521b1762d1e746f4c368f65e81a0e5308e2772dac85e",
"package.json": "36bcff8a760c08674f3fc017bb5affc8a5f4f39174ac9d5a29298f2423155832",
"README.md": "597cfb8da873e019d2658420f18ec059f0185fa9106ff9eafe934dfdd6ed266b",
"START-HERE.txt": "a549295cf57ac73673dc4fa6106463450f982122793cb9c7135ce4fc7087e9ed",
"THIRD-PARTY-NOTICES.txt": "32bbbd437f15b67690c6704fc14e2f12ae0fd463aa2eb5e4c99c1d61857f87fb",
"SHA256SUMS.txt": "37a2802abb67369f014270a7814e75dd00b26c1bb9c3ee37decf5eb3bacf695b"
}
}
@@ -0,0 +1,18 @@
# Independent final compact Help release-evidence review
Bounded acceptance: no material blocker found in the reviewed #39 release claims or evidence packet. This accepts scoped Windows development-preview evidence, not complete OA/SA acceptance, a final full-suite pass, publication confirmation, or the separate #46 correction.
Reviewed `harness/release-compact-help-validation-20261001.md`, top checkpoint in `harness/STATE.md`, `.local/package39/release-notes.md`, copied package packet and linked contribution records. Read-only source/file/JSON checks; no compiler, GUI, server launch, Gitea operation or tracked-file edit. This review file is the only new artifact.
Independent checks:
- All 133 entries of `package/evidence-files.json` match exact sizes and SHA256. The packet has 134 files; only the inventory itself is unlisted. Corresponding copied files match local originals. Reviewed relative Markdown links resolve.
- Actual ZIP is 34,184,534 bytes, SHA256 `18af75c7aea60bb0d5db5b1925d0d0815ecd08afc5bb258438851cfda809a6f2`. All 565 current extracted-file hashes equal the extraction record.
- Desktop `6e7c4dda`, reused server `715036e1`, and package-documentation `ea23e229` roles are explicit. Git comparison confirms unchanged core/server/Cargo/vendor inputs; the desktop-to-documentation delta contains evidence only. Runtime pairing explicitly uses the new selected DLL and fresh recorded server checks.
- Final focused log: 125 passed, zero failures/ignores, 1,421 filtered out. Earlier 5657 full-suite log: 1,538 passed, one failed synthetic typing precondition, seven ignores. Documents retain this distinction and corrected-test evidence.
- Native reports and receipts support scoped Help/caret/minimum-window/edit/save/history/reopen, two normal exits with empty logs, and retained source oracles. Same-folder/new-entry refusal remains visible and correctly classified as destination protection with misleading wording tracked in #46. Recovery here means normal Save All/fresh-destination continuation; no interrupted-transaction recovery claim is inferred.
- Reused prior independent JSON/byte reconciliation: both complete source receipts equal requests and actual disk bundles; complete saved Project differs only in Activity `61f7bbfb-74a4-5540-9182-2583dfb27a49` name. All 37 destination files match pristine bytes after one name-literal change and entry relocation. The 30 application-written source files and seven separately copied companions are distinguished.
- Corrected server result retains 23 checks, 11 exchanges, exact executable/runtime pins and forced TerminateProcess exits. Restart acceptance is limited to acknowledged-write persistence, excluding graceful shutdown and power-loss safety. Original Unicode-fixture error remains preserved; corrected-run evidence is separate.
- User-deferred Linux/WSL, one-machine/account scope, broad accessibility/DPI gaps, incomplete OA/SA and excluded peer increments remain explicit. Extra model warning after rename is acknowledged.
No reviewed document asserts that the new tag/release/assets have already been published. Release notes are prospective release copy; publication and asset readback still require separate confirmation. Existing published releases remain frozen.
@@ -0,0 +1,5 @@
{
"Name": "VMware SVGA 3D",
"DriverVersion": "9.17.11.4",
"AdapterRAM": 268435456
}
@@ -0,0 +1,22 @@
from pathlib import Path
import datetime,hashlib,json,os,platform,subprocess
root=Path(__file__).resolve().parent
record=json.loads((root/'extraction-check.json').read_text(encoding='utf-8'))
bundle=Path(record['bundle'])
run=root/'native-reopen';run.mkdir(exist_ok=False)
work=root/'working'
sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
env=os.environ.copy();system=os.environ['SystemRoot'];env['PATH']=str(Path(system)/'System32')+';'+system
for k in list(env):
if k.startswith(('CARGO_','RUST','LLVM','WGPU','ARCADIA_')):del env[k]
cmd=[str(bundle/'arcadia-studio.exe')]
start={'source':'6e7c4ddaf0775198384a8c4882809ea8e14b2976','package_source':'ea23e229260d7d050b3c3b88e77c3a851e936abe','start':datetime.datetime.now(datetime.timezone.utc).isoformat(),'command':cmd,'cwd':str(work),'path':env['PATH'],'platform':platform.platform(),'binary_sha256':sha(Path(cmd[0])),'runtime_sha256':sha(bundle/'libunwind.dll')}
assert start['binary_sha256']==record['sha256']['arcadia-studio.exe']
with (run/'stdout.log').open('xb') as out,(run/'stderr.log').open('xb') as err:
child=subprocess.Popen(cmd,cwd=work,env=env,stdout=out,stderr=err,creationflags=subprocess.CREATE_NO_WINDOW)
start['pid']=child.pid;(run/'started.json').write_text(json.dumps(start,indent=2)+'\n',encoding='utf-8');print(json.dumps(start,indent=2),flush=True)
code=child.wait()
inventory={p.relative_to(bundle).as_posix():sha(p) for p in bundle.rglob('*') if p.is_file()}
finish={**start,'exit_code':code,'finished':datetime.datetime.now(datetime.timezone.utc).isoformat(),'stdout_bytes':(run/'stdout.log').stat().st_size,'stderr_bytes':(run/'stderr.log').stat().st_size,'package_files_unchanged':inventory==record['sha256']}
(run/'finished.json').write_text(json.dumps(finish,indent=2)+'\n',encoding='utf-8');print(json.dumps(finish,indent=2),flush=True)
assert code==0 and finish['stdout_bytes']==finish['stderr_bytes']==0 and finish['package_files_unchanged']
@@ -0,0 +1,33 @@
from pathlib import Path
import datetime, hashlib, json, os, platform, subprocess
root = Path(__file__).resolve().parent
record = json.loads((root / 'extraction-check.json').read_text())
bundle = Path(record['bundle'])
run = root / 'native'
work = root / 'working'
sha = lambda p: hashlib.sha256(p.read_bytes()).hexdigest()
def inventory(p):
return {f.relative_to(p).as_posix(): sha(f) for f in p.rglob('*') if f.is_file()}
baseline = inventory(work / 'release1')
with (run / 'baseline.json').open('x', encoding='utf-8') as f:
json.dump(baseline, f, indent=2)
env = os.environ.copy()
system = os.environ['SystemRoot']
env['PATH'] = str(Path(system) / 'System32') + ';' + system
for key in list(env):
if key.startswith(('CARGO_', 'RUST', 'LLVM', 'WGPU', 'ARCADIA_')):
del env[key]
cmd = [str(bundle / 'arcadia-studio.exe')]
assert sha(Path(cmd[0])) == record['sha256']['arcadia-studio.exe']
assert sha(bundle / 'libunwind.dll') == record['sha256']['libunwind.dll']
receipt = {'source': '6e7c4ddaf0775198384a8c4882809ea8e14b2976', 'package_source': 'ea23e229260d7d050b3c3b88e77c3a851e936abe', 'start': datetime.datetime.now(datetime.timezone.utc).isoformat(), 'command': cmd, 'cwd': str(work), 'path': env['PATH'], 'platform': platform.platform(), 'binary_sha256': sha(Path(cmd[0])), 'runtime_sha256': sha(bundle / 'libunwind.dll')}
with (run / 'stdout.log').open('xb') as out, (run / 'stderr.log').open('xb') as err:
child = subprocess.Popen(cmd, cwd=work, env=env, stdout=out, stderr=err, creationflags=subprocess.CREATE_NO_WINDOW)
receipt['pid'] = child.pid
(run / 'started.json').write_text(json.dumps(receipt, indent=2)+'\n', encoding='utf-8')
print(json.dumps(receipt, indent=2), flush=True)
code = child.wait()
receipt.update({'exit_code': code, 'finished': datetime.datetime.now(datetime.timezone.utc).isoformat(), 'stdout_bytes': (run / 'stdout.log').stat().st_size, 'stderr_bytes': (run / 'stderr.log').stat().st_size, 'package_files_unchanged': inventory(bundle) == record['sha256']})
(run / 'finished.json').write_text(json.dumps(receipt, indent=2)+'\n', encoding='utf-8')
print(json.dumps(receipt, indent=2), flush=True)
assert code == 0 and receipt['stdout_bytes'] == receipt['stderr_bytes'] == 0 and receipt['package_files_unchanged']
@@ -0,0 +1 @@
{"issues":[{"code":"work_without_behavior_context","message":"Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.","object_id":"4149a872-3440-59de-9879-8981e78d27ab","severity":"warning"}],"revision":1,"valid":true}
@@ -0,0 +1,52 @@
{
"command": [
"C:\\ar-compact-01a0d717\\.local\\package39\\extracted\\ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm\\arcadia-server.exe",
"--bind",
"127.0.0.1:51265",
"--data-dir",
"C:\\ar-compact-01a0d717\\.local\\package39\\native-model-oracle\\store"
],
"pid": 3116,
"exit": 1,
"records": [
{
"label": "pristine",
"path": "/api/v1/projects/b1120b39-d420-496a-855a-b9ee0524b653/validation",
"response": {
"issues": [
{
"code": "work_without_behavior_context",
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"object_id": "4149a872-3440-59de-9879-8981e78d27ab",
"severity": "warning"
}
],
"revision": 1,
"valid": true
}
},
{
"label": "saved",
"path": "/api/v1/projects/0f2b9beb-fe51-4996-9367-568d77191729/validation",
"response": {
"issues": [
{
"code": "trace_drift",
"field": "baseline",
"message": "Linked descriptions changed since review (OaChanged); preview the transition to reconcile them.",
"object_id": "5309f955-53fd-5ff4-9c0a-91737e947341",
"severity": "warning"
},
{
"code": "work_without_behavior_context",
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"object_id": "4149a872-3440-59de-9879-8981e78d27ab",
"severity": "warning"
}
],
"revision": 1,
"valid": true
}
}
]
}
@@ -0,0 +1 @@
{"issues":[{"code":"trace_drift","field":"baseline","message":"Linked descriptions changed since review (OaChanged); preview the transition to reconcile them.","object_id":"5309f955-53fd-5ff4-9c0a-91737e947341","severity":"warning"},{"code":"work_without_behavior_context","message":"Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.","object_id":"4149a872-3440-59de-9879-8981e78d27ab","severity":"warning"}],"revision":1,"valid":true}
@@ -0,0 +1 @@
arcadia-server 0.2.0 listening on 127.0.0.1:51265; data C:\ar-compact-01a0d717\.local\package39\native-model-oracle\store
@@ -0,0 +1,58 @@
"""Decode full pristine/saved source bundles with the exact packaged server."""
from pathlib import Path
import copy, hashlib, http.client, json, os, socket, subprocess, time
root=Path(__file__).resolve().parent
proof=json.loads((root/'extraction-check.json').read_text(encoding='utf-8'))
bundle=Path(proof['bundle'])
run=root/'native-model-oracle';run.mkdir(exist_ok=False)
exe=bundle/'arcadia-server.exe'
assert hashlib.sha256(exe.read_bytes()).hexdigest()==proof['sha256']['arcadia-server.exe']
assert hashlib.sha256((bundle/'libunwind.dll').read_bytes()).hexdigest()==proof['sha256']['libunwind.dll']
env={k:os.environ[k] for k in ['SystemRoot','WINDIR','TEMP','TMP','USERPROFILE','LOCALAPPDATA','APPDATA','COMSPEC','OS','PROCESSOR_ARCHITECTURE'] if k in os.environ}
env['PATH']=str(Path(os.environ['SystemRoot'])/'System32')+';'+os.environ['SystemRoot']
with socket.socket() as s:s.bind(('127.0.0.1',0));port=s.getsockname()[1]
cmd=[str(exe),'--bind',f'127.0.0.1:{port}','--data-dir',str(run/'store')]
report={'status':'RUNNING','command':cmd,'path':env['PATH'],'server_sha256':proof['sha256']['arcadia-server.exe'],'runtime_sha256':proof['sha256']['libunwind.dll']}
counter=0
def request(method,path,body=None):
global counter
counter+=1
raw=b'' if body is None else json.dumps(body,ensure_ascii=False).encode('utf-8')
(run/f'{counter:02d}.request.json').write_bytes(raw)
c=http.client.HTTPConnection('127.0.0.1',port,timeout=10)
c.request(method,path,body=raw or None,headers={'Content-Type':'application/json','Connection':'close'})
r=c.getresponse();data=r.read();status=r.status
(run/f'{counter:02d}.response.json').write_bytes(data);c.close()
assert status in (200,201),(status,data)
return json.loads(data)
with (run/'stdout.log').open('xb') as out,(run/'stderr.log').open('xb') as err:
child=subprocess.Popen(cmd,cwd=run,env=env,stdout=out,stderr=err,creationflags=subprocess.CREATE_NO_WINDOW)
report['pid']=child.pid
try:
for _ in range(100):
assert child.poll() is None
try:
with socket.create_connection(('127.0.0.1',port),timeout=.1):break
except OSError:time.sleep(.1)
request('GET','/health/ready')
models=[]
source_records=[]
for folder,entry in [(bundle/'examples/release1','project.arcadia'),(root/'working/saved','qa-saved.arcadia')]:
files={p.relative_to(folder).as_posix():p.read_bytes().decode('utf-8') for p in folder.rglob('*.arcadia')}
assert len(files)==30
source={'entry':entry,'files':files}
imported=request('POST','/api/v1/projects/import-source-project',source)
envelope=request('GET','/api/v1/projects/'+imported['resource_id'])
assert envelope['source_project']==source
models.append(envelope['model']);source_records.append(imported['resource_id'])
expected=copy.deepcopy(models[0])
target='61f7bbfb-74a4-5540-9182-2583dfb27a49'
assert expected['elements'][target]['name']=='Assess pressure alarm'
expected['elements'][target]['name']='Assess pressure alarm QA LEFT XYRIGHT'
assert models[1]==expected,'Complete saved Project must have only declared Activity-name delta'
report.update({'status':'PASS','resource_ids':source_records,'source_files_each':30,'model_delta':{'id':target,'field':'name','before':'Assess pressure alarm','after':'Assess pressure alarm QA LEFT XYRIGHT'},'complete_project_equal_to_declared_delta':True,'source_preserved_exactly':True})
finally:
child.terminate();report['server_exit']=child.wait(timeout=15)
report['shutdown_scope']='Owned TerminateProcess after acknowledged import/read; no graceful shutdown claim.'
(run/'result.json').write_text(json.dumps(report,indent=2)+'\n',encoding='utf-8')
print(json.dumps(report,indent=2))
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"elements":94,"migration":null,"model_id":"2786d72f-5763-5f33-a57f-2773a4ca1a6c","name":"Restore safe water pressure — release 1 walkthrough","relations":62,"resource_id":"b1120b39-d420-496a-855a-b9ee0524b653","revision":1,"schema_version":7}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"elements":94,"migration":null,"model_id":"2786d72f-5763-5f33-a57f-2773a4ca1a6c","name":"Restore safe water pressure — release 1 walkthrough","relations":62,"resource_id":"0f2b9beb-fe51-4996-9367-568d77191729","revision":1,"schema_version":7}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,29 @@
{
"status": "PASS",
"command": [
"C:\\ar-compact-01a0d717\\.local\\package39\\extracted\\ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm\\arcadia-server.exe",
"--bind",
"127.0.0.1:60533",
"--data-dir",
"C:\\ar-compact-01a0d717\\.local\\package39\\native-model-oracle\\store"
],
"path": "C:\\Windows\\System32;C:\\Windows",
"server_sha256": "6cebacc117e46fdab023c6402a5cebd6929fb55993ae8ff6b89cd1c6c1170237",
"runtime_sha256": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4",
"pid": 7304,
"resource_ids": [
"b1120b39-d420-496a-855a-b9ee0524b653",
"0f2b9beb-fe51-4996-9367-568d77191729"
],
"source_files_each": 30,
"model_delta": {
"id": "61f7bbfb-74a4-5540-9182-2583dfb27a49",
"field": "name",
"before": "Assess pressure alarm",
"after": "Assess pressure alarm QA LEFT XYRIGHT"
},
"complete_project_equal_to_declared_delta": true,
"source_preserved_exactly": true,
"server_exit": 1,
"shutdown_scope": "Owned TerminateProcess after acknowledged import/read; no graceful shutdown claim."
}
@@ -0,0 +1 @@
arcadia-server 0.2.0 listening on 127.0.0.1:60533; data C:\ar-compact-01a0d717\.local\package39\native-model-oracle\store
Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

@@ -0,0 +1,19 @@
{
"source": "6e7c4ddaf0775198384a8c4882809ea8e14b2976",
"package_source": "ea23e229260d7d050b3c3b88e77c3a851e936abe",
"start": "2026-10-02T01:03:36.562376+00:00",
"command": [
"C:\\ar-compact-01a0d717\\.local\\package39\\extracted\\ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm\\arcadia-studio.exe"
],
"cwd": "C:\\ar-compact-01a0d717\\.local\\package39\\working",
"path": "C:\\Windows\\System32;C:\\Windows",
"platform": "Windows-11-10.0.26200-SP0",
"binary_sha256": "d55ecad4b0f75c0c9c26625e5441bef9c9360890b65bc13236c6ebfaf03d631e",
"runtime_sha256": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4",
"pid": 3268,
"exit_code": 0,
"finished": "2026-10-02T01:05:26.044448+00:00",
"stdout_bytes": 0,
"stderr_bytes": 0,
"package_files_unchanged": true
}
@@ -0,0 +1,16 @@
# Fresh-process package reopen review
Independent native review by `/root/ux_critique`, 2026-10-01 America/Los_Angeles, using Computer Use `@oai/sky`. Continuation of [the first package session](../native/review.md).
Parent launched fresh PID 3268 from the same extracted `ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm/arcadia-studio.exe`, with system-only PATH and an unrelated cwd. Desktop source is `6e7c4ddaf0775198384a8c4882809ea8e14b2976`; executable SHA-256 is `D55ECAD4B0F75C0C9C26625E5441BEF9C9360890B65BC13236C6EBFAF03D631E`. Reviewer independently selected the newly returned native app window; no launch or kill was performed by the reviewer.
## Actual workflow and result
1. From fresh Home, clicked pointer Open and entered exactly `C:/ar-compact-01a0d717/.local/package39/working/saved/qa-saved.arcadia` in the native file dialog. Opened that entry, without using recent/recovery shortcuts. Evidence: `01-reopen-exact-path.png`.
2. OA Allocation loaded with `All changes saved`, 94 objects and 62 links. Clicked the renamed Assess activity. Inspector displayed exact Name `Assess pressure alarm QA LEFT XYRIGHT`, stable alias `oa_assess`, Performer `Control room`, Parent `Restore safe pressure`, and identity `61f7bbfb-74a4-5540-9182-2583dfb27a49`. Evidence: `02-reopened-exact-name-properties.png`.
3. The UI displayed two model checks, matching the first session after the declared rename, whereas the pristine model displayed one. This observation is retained rather than claiming every count stayed identical. No new model or property changes were made during this reopen session.
4. Opened F1 Help, observed the ownership/allocation topic, and used Escape to return. The same activity remained selected with the persisted properties and canvas context. Evidence: `03-reopened-help.png`.
5. Clicked Save all without editing. UI remained saved and footer reported `All source files saved`. Evidence: `04-reopened-no-change-save.png`. Byte-for-byte no-op validation is the parent's independent oracle, not inferred from this status.
6. Closed normally with Alt+F4. Fresh native window enumeration confirmed no extracted-package app window remained. No crash dialog was observed. Parent was notified that the GUI is available for the separate #38 review; reviewer did not interact with #38 or any other application.
No blocking native defect was observed in this reopen/no-change-save scope. Parent owns the second-process exit/stdout/stderr checks and the post-close full-file comparison. Screenshots and visible properties support the interaction findings; they do not prove complete model equality by themselves.
@@ -0,0 +1,14 @@
{
"source": "6e7c4ddaf0775198384a8c4882809ea8e14b2976",
"package_source": "ea23e229260d7d050b3c3b88e77c3a851e936abe",
"start": "2026-10-02T01:03:36.562376+00:00",
"command": [
"C:\\ar-compact-01a0d717\\.local\\package39\\extracted\\ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm\\arcadia-studio.exe"
],
"cwd": "C:\\ar-compact-01a0d717\\.local\\package39\\working",
"path": "C:\\Windows\\System32;C:\\Windows",
"platform": "Windows-11-10.0.26200-SP0",
"binary_sha256": "d55ecad4b0f75c0c9c26625e5441bef9c9360890b65bc13236c6ebfaf03d631e",
"runtime_sha256": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4",
"pid": 3268
}
@@ -0,0 +1,46 @@
{
"status": "PASS",
"files": 37,
"source_files": 30,
"oracle": "Complete pristine37-file project with entry relocated to qa-saved.arcadia and exactlyone declared Activity-name literal; seven non-source companions separately copied before SaveAs,30 sourcefiles written by application.",
"differences": [],
"sha256": {
"expected-diagnostics.json": "3700393d76b940cfc076bf52861bd7b1f43370f8eabce3594531bd0bf1fc851e",
"model.json": "ef0ef4006c3e09d5db6e1b621256b3f7d6642f3a7b09cc34607cfbcca830c727",
"model.rs": "ddc2d92b0d7699a4cadfbb35ae379f0a68d17f4f5d7df82b4b1afcf8d5425fbe",
"qa-saved.arcadia": "b2674e6bf77cfe0f6f0ba79c81d2e96d92f39c02ca2c6266d80e91723bfb9fda",
"README.md": "113cfb1cf38a98b51bff60442d86a35d636a3d80ef206bece7d53d0aae67f2d5",
"traceability.arcadia": "474bd28d955ff7af7722f6e9a23220d816879e3f46202e79a233544b9c593116",
"transition-before.json": "2253e6434f741687e22327c37d520ed15c043bc36025498650bc2366bb6a874d",
"transition-request.json": "43b9a22e5f95ad3bc0ebd2478ead0e14406854abc989ae94ded9367b013d812a",
"evidence/repair-check.md": "f4db17bdca4ecbb1bf0caef01b2da5f1f7f798ab3152b648ce6dd8696a6a68e5",
"oa/capabilities.arcadia": "26a3d9b615a3251ff010e0b47127f2d16bc2dcd45ed95967208b0076d725843a",
"oa/model.arcadia": "069a5eaaf525089935cc9d73ae7b916866ed78c6534705d75739650ecdea7ada",
"oa/participants.arcadia": "591a75a9ecad280302be4d4ae0171ad616377ed11ead5e233848ca2dc0a71819",
"oa/types.arcadia": "f02c391d584674c879f662a418abba3a220f1fee88471ca49322ac9873de628d",
"sa/capabilities.arcadia": "81706a0ca9d7db2aa0350888b9c00d853885185db2e6b90557d3d51a11c3d645",
"sa/model.arcadia": "805b90262e27090ae7da81a51bcefa49c97fb0b3d3220b4a72323b4c4351c395",
"sa/participants.arcadia": "ca144d6a8f45fe720b5d18c7b59c7671444baa8c37e5ce9117bcba34b3a6aa84",
"sa/types.arcadia": "540ddbc2bcd0ccf45298e3ea5660637341aff63ff65fd87ac2f26469d1adc064",
"views/diagram_oa_allocation_view.arcadia": "c45d7467839219d894804c1ef2d22ff4d4a5a84a6a273c526bafd4c1facde624",
"views/diagram_sa_allocation_view.arcadia": "352a3ca745517592b5c9b1239b5770f62f0466a329a91fd03457ef2b53c06ed6",
"views/diagram_sa_capability_view.arcadia": "8f18fcccbcddcf3c1ea0ffe727b5b23b77fc148c5da4fde953abe681e4c20cd2",
"views/diagram_sa_service_view.arcadia": "4c72cb7593d9c4be8d2600b0656a7a3a6ce84c45bb2f3e58a4ee5ae3bf91913c",
"views/diagram_sa_state_view.arcadia": "dd1bea81ac7dc549a53f9b190be6f13052e00ede345bd180444cc31edb892230",
"oa/activities/activity_oa_restore_work.arcadia": "131451be326e44ccf022996246cc341a32040c7ac6f56b4db4202e6c259cfc0a",
"oa/behavior/process_oa_assessment_procedure.arcadia": "23cabc213c264a16c8595414db6acb09248c1cef9cd699f1b0316e5bb82c062a",
"oa/behavior/process_oa_field_procedure.arcadia": "9c52086759b7344b4809ceb8d372f65647b16fc24153a03b887faa378c37fe96",
"oa/behavior/process_oa_response.arcadia": "675798bd19e6b40d03e7076b0e59b110cdaaab7b6d16c47c3ac80fa960072e84",
"oa/behavior/scenario_oa_conversation.arcadia": "e217e5394a0771dc16206ed9437a231da27905d2f19be64b3f48281c0a32fb6b",
"oa/behavior/scenario_oa_full_scenario.arcadia": "c5a285f2d7c61bd2a136107f267fbf9cddb1cba3f63ff36a6f53f7ab98f33df5",
"oa/behavior/state_machine_oa_monitor.arcadia": "e7349e1e5336282eed3f5a103129db33d3ae4ed500788ee7129e8fc193ac57f6",
"sa/behavior/process_sa_assessment_procedure.arcadia": "5c414fbdaabf9f9e402b0441fcacf1cacf4dbbc6c822162f028de35abf59be2d",
"sa/behavior/process_sa_field_procedure.arcadia": "ece846fdf93857e71a9ab0efcbdf64a5c41c71180f62afa3f79077fffb53b8f2",
"sa/behavior/process_sa_response.arcadia": "af7fdda280a917cf626f6e077162d481f9aaad280f8e81330c07d7fe384b1c52",
"sa/behavior/scenario_sa_conversation.arcadia": "01339699ec3ee409418d9bb232542f2bcd9419f1e67b183e77700c56bb9f71d2",
"sa/behavior/scenario_sa_full_scenario.arcadia": "5dce6b00a3a441162e8dee3e23d9ca5ab7bed4a24de669b1fba03664a3086dda",
"sa/behavior/state_machine_sa_monitor.arcadia": "17dbcfa5bbfd10fc8e23a325fc950ce853c219b4aa89c74f96278cbb3f34acc8",
"sa/functions/function_sa_audit_thresholds.arcadia": "27f798f78f36e72b38edab3725af2feb0eaa010fd52a60b5c6b6bef57ad3b686",
"sa/functions/function_sa_restore_work.arcadia": "357c6acb80f86b4e8d8f5c98f9f79d140f85fbe09f3406b41b7ac6eba464ec8c"
}
}
@@ -0,0 +1,46 @@
{
"status": "PASS",
"files": 37,
"source_files": 30,
"oracle": "Complete pristine37-file project with entry relocated to qa-saved.arcadia and exactlyone declared Activity-name literal; seven non-source companions separately copied before SaveAs,30 sourcefiles written by application.",
"differences": [],
"sha256": {
"expected-diagnostics.json": "3700393d76b940cfc076bf52861bd7b1f43370f8eabce3594531bd0bf1fc851e",
"model.json": "ef0ef4006c3e09d5db6e1b621256b3f7d6642f3a7b09cc34607cfbcca830c727",
"model.rs": "ddc2d92b0d7699a4cadfbb35ae379f0a68d17f4f5d7df82b4b1afcf8d5425fbe",
"qa-saved.arcadia": "b2674e6bf77cfe0f6f0ba79c81d2e96d92f39c02ca2c6266d80e91723bfb9fda",
"README.md": "113cfb1cf38a98b51bff60442d86a35d636a3d80ef206bece7d53d0aae67f2d5",
"traceability.arcadia": "474bd28d955ff7af7722f6e9a23220d816879e3f46202e79a233544b9c593116",
"transition-before.json": "2253e6434f741687e22327c37d520ed15c043bc36025498650bc2366bb6a874d",
"transition-request.json": "43b9a22e5f95ad3bc0ebd2478ead0e14406854abc989ae94ded9367b013d812a",
"evidence/repair-check.md": "f4db17bdca4ecbb1bf0caef01b2da5f1f7f798ab3152b648ce6dd8696a6a68e5",
"oa/capabilities.arcadia": "26a3d9b615a3251ff010e0b47127f2d16bc2dcd45ed95967208b0076d725843a",
"oa/model.arcadia": "069a5eaaf525089935cc9d73ae7b916866ed78c6534705d75739650ecdea7ada",
"oa/participants.arcadia": "591a75a9ecad280302be4d4ae0171ad616377ed11ead5e233848ca2dc0a71819",
"oa/types.arcadia": "f02c391d584674c879f662a418abba3a220f1fee88471ca49322ac9873de628d",
"sa/capabilities.arcadia": "81706a0ca9d7db2aa0350888b9c00d853885185db2e6b90557d3d51a11c3d645",
"sa/model.arcadia": "805b90262e27090ae7da81a51bcefa49c97fb0b3d3220b4a72323b4c4351c395",
"sa/participants.arcadia": "ca144d6a8f45fe720b5d18c7b59c7671444baa8c37e5ce9117bcba34b3a6aa84",
"sa/types.arcadia": "540ddbc2bcd0ccf45298e3ea5660637341aff63ff65fd87ac2f26469d1adc064",
"views/diagram_oa_allocation_view.arcadia": "c45d7467839219d894804c1ef2d22ff4d4a5a84a6a273c526bafd4c1facde624",
"views/diagram_sa_allocation_view.arcadia": "352a3ca745517592b5c9b1239b5770f62f0466a329a91fd03457ef2b53c06ed6",
"views/diagram_sa_capability_view.arcadia": "8f18fcccbcddcf3c1ea0ffe727b5b23b77fc148c5da4fde953abe681e4c20cd2",
"views/diagram_sa_service_view.arcadia": "4c72cb7593d9c4be8d2600b0656a7a3a6ce84c45bb2f3e58a4ee5ae3bf91913c",
"views/diagram_sa_state_view.arcadia": "dd1bea81ac7dc549a53f9b190be6f13052e00ede345bd180444cc31edb892230",
"oa/activities/activity_oa_restore_work.arcadia": "131451be326e44ccf022996246cc341a32040c7ac6f56b4db4202e6c259cfc0a",
"oa/behavior/process_oa_assessment_procedure.arcadia": "23cabc213c264a16c8595414db6acb09248c1cef9cd699f1b0316e5bb82c062a",
"oa/behavior/process_oa_field_procedure.arcadia": "9c52086759b7344b4809ceb8d372f65647b16fc24153a03b887faa378c37fe96",
"oa/behavior/process_oa_response.arcadia": "675798bd19e6b40d03e7076b0e59b110cdaaab7b6d16c47c3ac80fa960072e84",
"oa/behavior/scenario_oa_conversation.arcadia": "e217e5394a0771dc16206ed9437a231da27905d2f19be64b3f48281c0a32fb6b",
"oa/behavior/scenario_oa_full_scenario.arcadia": "c5a285f2d7c61bd2a136107f267fbf9cddb1cba3f63ff36a6f53f7ab98f33df5",
"oa/behavior/state_machine_oa_monitor.arcadia": "e7349e1e5336282eed3f5a103129db33d3ae4ed500788ee7129e8fc193ac57f6",
"sa/behavior/process_sa_assessment_procedure.arcadia": "5c414fbdaabf9f9e402b0441fcacf1cacf4dbbc6c822162f028de35abf59be2d",
"sa/behavior/process_sa_field_procedure.arcadia": "ece846fdf93857e71a9ab0efcbdf64a5c41c71180f62afa3f79077fffb53b8f2",
"sa/behavior/process_sa_response.arcadia": "af7fdda280a917cf626f6e077162d481f9aaad280f8e81330c07d7fe384b1c52",
"sa/behavior/scenario_sa_conversation.arcadia": "01339699ec3ee409418d9bb232542f2bcd9419f1e67b183e77700c56bb9f71d2",
"sa/behavior/scenario_sa_full_scenario.arcadia": "5dce6b00a3a441162e8dee3e23d9ca5ab7bed4a24de669b1fba03664a3086dda",
"sa/behavior/state_machine_sa_monitor.arcadia": "17dbcfa5bbfd10fc8e23a325fc950ce853c219b4aa89c74f96278cbb3f34acc8",
"sa/functions/function_sa_audit_thresholds.arcadia": "27f798f78f36e72b38edab3725af2feb0eaa010fd52a60b5c6b6bef57ad3b686",
"sa/functions/function_sa_restore_work.arcadia": "357c6acb80f86b4e8d8f5c98f9f79d140f85fbe09f3406b41b7ac6eba464ec8c"
}
}
@@ -0,0 +1,45 @@
{
"status": "PASS",
"oracle": "all37 files exactly equal pristine bundle plus only declared Activity-name literal",
"changed_file": "oa/activities/activity_oa_restore_work.arcadia",
"files": 37,
"sha256": {
"expected-diagnostics.json": "3700393d76b940cfc076bf52861bd7b1f43370f8eabce3594531bd0bf1fc851e",
"model.json": "ef0ef4006c3e09d5db6e1b621256b3f7d6642f3a7b09cc34607cfbcca830c727",
"model.rs": "ddc2d92b0d7699a4cadfbb35ae379f0a68d17f4f5d7df82b4b1afcf8d5425fbe",
"project.arcadia": "b2674e6bf77cfe0f6f0ba79c81d2e96d92f39c02ca2c6266d80e91723bfb9fda",
"README.md": "113cfb1cf38a98b51bff60442d86a35d636a3d80ef206bece7d53d0aae67f2d5",
"traceability.arcadia": "474bd28d955ff7af7722f6e9a23220d816879e3f46202e79a233544b9c593116",
"transition-before.json": "2253e6434f741687e22327c37d520ed15c043bc36025498650bc2366bb6a874d",
"transition-request.json": "43b9a22e5f95ad3bc0ebd2478ead0e14406854abc989ae94ded9367b013d812a",
"evidence/repair-check.md": "f4db17bdca4ecbb1bf0caef01b2da5f1f7f798ab3152b648ce6dd8696a6a68e5",
"oa/capabilities.arcadia": "26a3d9b615a3251ff010e0b47127f2d16bc2dcd45ed95967208b0076d725843a",
"oa/model.arcadia": "069a5eaaf525089935cc9d73ae7b916866ed78c6534705d75739650ecdea7ada",
"oa/participants.arcadia": "591a75a9ecad280302be4d4ae0171ad616377ed11ead5e233848ca2dc0a71819",
"oa/types.arcadia": "f02c391d584674c879f662a418abba3a220f1fee88471ca49322ac9873de628d",
"sa/capabilities.arcadia": "81706a0ca9d7db2aa0350888b9c00d853885185db2e6b90557d3d51a11c3d645",
"sa/model.arcadia": "805b90262e27090ae7da81a51bcefa49c97fb0b3d3220b4a72323b4c4351c395",
"sa/participants.arcadia": "ca144d6a8f45fe720b5d18c7b59c7671444baa8c37e5ce9117bcba34b3a6aa84",
"sa/types.arcadia": "540ddbc2bcd0ccf45298e3ea5660637341aff63ff65fd87ac2f26469d1adc064",
"views/diagram_oa_allocation_view.arcadia": "c45d7467839219d894804c1ef2d22ff4d4a5a84a6a273c526bafd4c1facde624",
"views/diagram_sa_allocation_view.arcadia": "352a3ca745517592b5c9b1239b5770f62f0466a329a91fd03457ef2b53c06ed6",
"views/diagram_sa_capability_view.arcadia": "8f18fcccbcddcf3c1ea0ffe727b5b23b77fc148c5da4fde953abe681e4c20cd2",
"views/diagram_sa_service_view.arcadia": "4c72cb7593d9c4be8d2600b0656a7a3a6ce84c45bb2f3e58a4ee5ae3bf91913c",
"views/diagram_sa_state_view.arcadia": "dd1bea81ac7dc549a53f9b190be6f13052e00ede345bd180444cc31edb892230",
"oa/activities/activity_oa_restore_work.arcadia": "131451be326e44ccf022996246cc341a32040c7ac6f56b4db4202e6c259cfc0a",
"oa/behavior/process_oa_assessment_procedure.arcadia": "23cabc213c264a16c8595414db6acb09248c1cef9cd699f1b0316e5bb82c062a",
"oa/behavior/process_oa_field_procedure.arcadia": "9c52086759b7344b4809ceb8d372f65647b16fc24153a03b887faa378c37fe96",
"oa/behavior/process_oa_response.arcadia": "675798bd19e6b40d03e7076b0e59b110cdaaab7b6d16c47c3ac80fa960072e84",
"oa/behavior/scenario_oa_conversation.arcadia": "e217e5394a0771dc16206ed9437a231da27905d2f19be64b3f48281c0a32fb6b",
"oa/behavior/scenario_oa_full_scenario.arcadia": "c5a285f2d7c61bd2a136107f267fbf9cddb1cba3f63ff36a6f53f7ab98f33df5",
"oa/behavior/state_machine_oa_monitor.arcadia": "e7349e1e5336282eed3f5a103129db33d3ae4ed500788ee7129e8fc193ac57f6",
"sa/behavior/process_sa_assessment_procedure.arcadia": "5c414fbdaabf9f9e402b0441fcacf1cacf4dbbc6c822162f028de35abf59be2d",
"sa/behavior/process_sa_field_procedure.arcadia": "ece846fdf93857e71a9ab0efcbdf64a5c41c71180f62afa3f79077fffb53b8f2",
"sa/behavior/process_sa_response.arcadia": "af7fdda280a917cf626f6e077162d481f9aaad280f8e81330c07d7fe384b1c52",
"sa/behavior/scenario_sa_conversation.arcadia": "01339699ec3ee409418d9bb232542f2bcd9419f1e67b183e77700c56bb9f71d2",
"sa/behavior/scenario_sa_full_scenario.arcadia": "5dce6b00a3a441162e8dee3e23d9ca5ab7bed4a24de669b1fba03664a3086dda",
"sa/behavior/state_machine_sa_monitor.arcadia": "17dbcfa5bbfd10fc8e23a325fc950ce853c219b4aa89c74f96278cbb3f34acc8",
"sa/functions/function_sa_audit_thresholds.arcadia": "27f798f78f36e72b38edab3725af2feb0eaa010fd52a60b5c6b6bef57ad3b686",
"sa/functions/function_sa_restore_work.arcadia": "357c6acb80f86b4e8d8f5c98f9f79d140f85fbe09f3406b41b7ac6eba464ec8c"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

@@ -0,0 +1,39 @@
{
"expected-diagnostics.json": "3700393d76b940cfc076bf52861bd7b1f43370f8eabce3594531bd0bf1fc851e",
"model.json": "ef0ef4006c3e09d5db6e1b621256b3f7d6642f3a7b09cc34607cfbcca830c727",
"model.rs": "ddc2d92b0d7699a4cadfbb35ae379f0a68d17f4f5d7df82b4b1afcf8d5425fbe",
"project.arcadia": "b2674e6bf77cfe0f6f0ba79c81d2e96d92f39c02ca2c6266d80e91723bfb9fda",
"README.md": "113cfb1cf38a98b51bff60442d86a35d636a3d80ef206bece7d53d0aae67f2d5",
"traceability.arcadia": "474bd28d955ff7af7722f6e9a23220d816879e3f46202e79a233544b9c593116",
"transition-before.json": "2253e6434f741687e22327c37d520ed15c043bc36025498650bc2366bb6a874d",
"transition-request.json": "43b9a22e5f95ad3bc0ebd2478ead0e14406854abc989ae94ded9367b013d812a",
"evidence/repair-check.md": "f4db17bdca4ecbb1bf0caef01b2da5f1f7f798ab3152b648ce6dd8696a6a68e5",
"oa/capabilities.arcadia": "26a3d9b615a3251ff010e0b47127f2d16bc2dcd45ed95967208b0076d725843a",
"oa/model.arcadia": "069a5eaaf525089935cc9d73ae7b916866ed78c6534705d75739650ecdea7ada",
"oa/participants.arcadia": "591a75a9ecad280302be4d4ae0171ad616377ed11ead5e233848ca2dc0a71819",
"oa/types.arcadia": "f02c391d584674c879f662a418abba3a220f1fee88471ca49322ac9873de628d",
"sa/capabilities.arcadia": "81706a0ca9d7db2aa0350888b9c00d853885185db2e6b90557d3d51a11c3d645",
"sa/model.arcadia": "805b90262e27090ae7da81a51bcefa49c97fb0b3d3220b4a72323b4c4351c395",
"sa/participants.arcadia": "ca144d6a8f45fe720b5d18c7b59c7671444baa8c37e5ce9117bcba34b3a6aa84",
"sa/types.arcadia": "540ddbc2bcd0ccf45298e3ea5660637341aff63ff65fd87ac2f26469d1adc064",
"views/diagram_oa_allocation_view.arcadia": "c45d7467839219d894804c1ef2d22ff4d4a5a84a6a273c526bafd4c1facde624",
"views/diagram_sa_allocation_view.arcadia": "352a3ca745517592b5c9b1239b5770f62f0466a329a91fd03457ef2b53c06ed6",
"views/diagram_sa_capability_view.arcadia": "8f18fcccbcddcf3c1ea0ffe727b5b23b77fc148c5da4fde953abe681e4c20cd2",
"views/diagram_sa_service_view.arcadia": "4c72cb7593d9c4be8d2600b0656a7a3a6ce84c45bb2f3e58a4ee5ae3bf91913c",
"views/diagram_sa_state_view.arcadia": "dd1bea81ac7dc549a53f9b190be6f13052e00ede345bd180444cc31edb892230",
"oa/activities/activity_oa_restore_work.arcadia": "21cbd7176af8fad68453306f2371ff03e127a994b8796cdf044567332de9686f",
"oa/behavior/process_oa_assessment_procedure.arcadia": "23cabc213c264a16c8595414db6acb09248c1cef9cd699f1b0316e5bb82c062a",
"oa/behavior/process_oa_field_procedure.arcadia": "9c52086759b7344b4809ceb8d372f65647b16fc24153a03b887faa378c37fe96",
"oa/behavior/process_oa_response.arcadia": "675798bd19e6b40d03e7076b0e59b110cdaaab7b6d16c47c3ac80fa960072e84",
"oa/behavior/scenario_oa_conversation.arcadia": "e217e5394a0771dc16206ed9437a231da27905d2f19be64b3f48281c0a32fb6b",
"oa/behavior/scenario_oa_full_scenario.arcadia": "c5a285f2d7c61bd2a136107f267fbf9cddb1cba3f63ff36a6f53f7ab98f33df5",
"oa/behavior/state_machine_oa_monitor.arcadia": "e7349e1e5336282eed3f5a103129db33d3ae4ed500788ee7129e8fc193ac57f6",
"sa/behavior/process_sa_assessment_procedure.arcadia": "5c414fbdaabf9f9e402b0441fcacf1cacf4dbbc6c822162f028de35abf59be2d",
"sa/behavior/process_sa_field_procedure.arcadia": "ece846fdf93857e71a9ab0efcbdf64a5c41c71180f62afa3f79077fffb53b8f2",
"sa/behavior/process_sa_response.arcadia": "af7fdda280a917cf626f6e077162d481f9aaad280f8e81330c07d7fe384b1c52",
"sa/behavior/scenario_sa_conversation.arcadia": "01339699ec3ee409418d9bb232542f2bcd9419f1e67b183e77700c56bb9f71d2",
"sa/behavior/scenario_sa_full_scenario.arcadia": "5dce6b00a3a441162e8dee3e23d9ca5ab7bed4a24de669b1fba03664a3086dda",
"sa/behavior/state_machine_sa_monitor.arcadia": "17dbcfa5bbfd10fc8e23a325fc950ce853c219b4aa89c74f96278cbb3f34acc8",
"sa/functions/function_sa_audit_thresholds.arcadia": "27f798f78f36e72b38edab3725af2feb0eaa010fd52a60b5c6b6bef57ad3b686",
"sa/functions/function_sa_restore_work.arcadia": "357c6acb80f86b4e8d8f5c98f9f79d140f85fbe09f3406b41b7ac6eba464ec8c"
}
@@ -0,0 +1,19 @@
{
"source": "6e7c4ddaf0775198384a8c4882809ea8e14b2976",
"package_source": "ea23e229260d7d050b3c3b88e77c3a851e936abe",
"start": "2026-10-02T00:53:58.506330+00:00",
"command": [
"C:\\ar-compact-01a0d717\\.local\\package39\\extracted\\ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm\\arcadia-studio.exe"
],
"cwd": "C:\\ar-compact-01a0d717\\.local\\package39\\working",
"path": "C:\\Windows\\System32;C:\\Windows",
"platform": "Windows-11-10.0.26200-SP0",
"binary_sha256": "d55ecad4b0f75c0c9c26625e5441bef9c9360890b65bc13236c6ebfaf03d631e",
"runtime_sha256": "f45604a12e8bcd839fd86f667c1be1cc7c4f9da2b3c69ff381ac708f0e1ae7d4",
"pid": 8120,
"exit_code": 0,
"finished": "2026-10-02T01:03:10.515045+00:00",
"stdout_bytes": 0,
"stderr_bytes": 0,
"package_files_unchanged": true
}
@@ -0,0 +1,54 @@
# Extracted package native review
Independent native review by `/root/ux_critique`, 2026-10-01 America/Los_Angeles. Uses Computer Use `@oai/sky` only for application actions; skill version 26.928.40906 and its mandatory docs were read. Parent launched the extracted package with system-only PATH and an unrelated working directory, and owns stdout/stderr/exit and byte-oracle checks.
## Identity
- Desktop source: `6e7c4ddaf0775198384a8c4882809ea8e14b2976`; package/documentation revision supplied by parent: `ea23`.
- Executable SHA-256 supplied by parent: `D55ECAD4B0F75C0C9C26625E5441BEF9C9360890B65BC13236C6EBFAF03D631E`.
- Extracted executable selected from native window enumeration: `C:/ar-compact-01a0d717/.local/package39/extracted/ArcadiaStudio-0.2.0-preview.compact-help.20261001-x86_64-pc-windows-gnullvm/arcadia-studio.exe`.
- Parent-owned first process: PID 8120. Reviewer did not launch or kill it.
## Passed native observations
1. Home loaded at normal 1440x900 client size (1442x932 captured). Clicked `Open water-pressure walkthrough`. The packaged example opened in OA Allocation with 94 objects, 62 links, one model check and `All changes saved`. No edit or Save was made to the bundled example. Evidence: `01-package-home.png`, `02-bundled-walkthrough.png`.
2. Clicked the visible `Help · F1` control. The in-app ownership/allocation guide appeared and was readable without a browser; Escape returned to the example. Evidence: `03-bundled-offline-help.png`. This verifies bundled Help availability, not a network isolation experiment.
3. Used pointer Open and the native file dialog to open exactly `C:/ar-compact-01a0d717/.local/package39/working/release1/project.arcadia`, the complete separate copied fixture. Native dialog path was explicitly entered; no recent-model shortcut or original fixture was used for editing.
4. Resized through the native window system menu to 1100x700 client (1102x732 captured). Projection, Help, Arrange, Destination, selection status, Inspector Apply/Revert and Save As remained reachable. Evidence: `04-working-minimum.png`.
5. Clicked Assess pressure alarm in the canvas and edited only its Name draft to `Assess pressure alarm QA LEFT RIGHT`. Ctrl+Left placed the caret before RIGHT. F1, PageDown, End, Escape, then X without refocusing produced LEFT XRIGHT. F1, visible Close, then Y without refocusing produced exactly `Assess pressure alarm QA LEFT XYRIGHT`. The selected activity, canvas and zoom stayed in place. Evidence: `05-minimum-help-keyboard.png`, `06-exact-draft-left-xyright.png`.
6. Clicked Apply changes. The displayed Name became exactly `Assess pressure alarm QA LEFT XYRIGHT`; alias remained `oa_assess`; performer remained Control room. No other authored edit was performed. Evidence: `07-qa-rename-applied.png`.
## Observed refusal: Save As reports an external-change conflict
Reproduction on the first process:
1. After the successful Apply above, clicked Save As.
2. The native Save As dialog opened in the complete working-copy `release1` folder. Entered the full target `C:/ar-compact-01a0d717/.local/package39/working/release1/qa-saved.arcadia` and clicked Save. Evidence: `08-save-as-path.png`.
3. The dialog closed, but the application remained `Unsaved changes` with title `*`. The visible footer reported: `Source files changed outside Arcadia Studio: ["oa/activities/activity_oa_restore_work.arcadia"]`. A second fresh native observation showed the same state. Evidence: `09-save-as-unexpected-external-change.png`.
The reviewer stopped dependent actions immediately and notified the parent. No further input was issued until the parent inspected disk and authorized the recovery below. The parent reported all 37 copied files still byte-identical and no qa-saved entry. Parent source inspection indicated a protected-destination contract may intentionally refuse this same-root new-entry Save As; independent classification was pending. The misleading external-change wording is preserved as observed, without claiming a root cause or data loss.
## Authorized recovery: save existing entry and Undo/Redo
After the parent's disk check and explicit revised instructions:
1. Clicked Save all to the existing copied `project.arcadia`. The application showed `All changes saved`, removed the title asterisk, and footer reported `All source files saved`. Exact QA name remained. Evidence: `10-save-existing-project-success.png`.
2. Pressed Ctrl+Z. Name returned to `Assess pressure alarm`, title gained the asterisk, and UI showed `Unsaved changes` / `Model change undone`. Evidence: `11-undo-original-unsaved.png`.
3. Pressed Ctrl+Y. Exact QA name returned, UI returned to `All changes saved`, and footer reported `Model change redone`. Evidence: `12-redo-renamed-saved.png`.
4. Clicked Save all again. After Working completed, footer reported `All source files saved`; saved status and exact QA name remained. Evidence: `13-save-after-redo.png`.
No other authored edits were made. Parent subsequently reported its full 37-file oracle matched the pristine copy plus only the declared Activity name literal. Independent source/spec review classified the earlier refusal as intentional destination collision protection, with misleading external-change wording as a separate diagnostic issue.
## Fresh-destination Save As
Parent prepared `C:/ar-compact-01a0d717/.local/package39/working/saved/` with the seven non-source tutorial/evidence companions and no `.arcadia` files, then authorized another Save As.
1. Clicked Save As and entered exactly `C:/ar-compact-01a0d717/.local/package39/working/saved/qa-saved.arcadia`. Evidence: `14-fresh-save-as-path.png`.
2. Clicked Save. After Working completed, the UI showed `All changes saved` and `All source files saved`; the exact QA name remained in the Inspector. Evidence: `15-fresh-save-as-success.png`.
3. Closed the application normally with Alt+F4. A subsequent fresh native window enumeration showed no extracted-package app window. No crash dialog was observed; exit status is the parent's watcher evidence.
## Independent checks and continuation
Parent reported first-process exit 0 with empty stdout/stderr, all 565 extracted-package files unchanged, and all 37 fresh-destination files matching the pristine bundle plus only the declared Activity name and relocated entry. The application wrote the 30 source files; parent had copied seven non-source companions. These are attributed parent checks, separate from this native review.
The [second-process reopen/no-change Save](../native-reopen/review.md) was subsequently exercised successfully on the same extracted executable. It verifies the persisted exact name, alias, performer and model counts, Help return, no-edit Save completion and normal closure. The parent owns its post-close byte oracle and watcher results. The original protected-destination refusal and recovery above remain part of the evidence; no source corruption or atomicity failure is asserted from that refusal.

Some files were not shown because too many files have changed in this diff Show More