Prepare complete-scene diagram readability fixtures and probe

This commit is contained in:
2026-10-01 17:42:24 -07:00
parent 715036e132
commit 2aac3e8353
11 changed files with 2560 additions and 0 deletions
@@ -0,0 +1,548 @@
//! Experimental geometry evidence, not a native selection or usability oracle.
//! See harness/experiments/diagram-readability-20260929/README.md.
use oa_core::{Diagram, Id, Position, Project, persistence, projection, routing, validation};
use serde_json::{Value, json};
use std::{
error::Error,
fs::{self, OpenOptions},
io::Write,
path::Path,
};
type Result<T> = std::result::Result<T, Box<dyn Error>>;
const MIXED: &[u8] = include_bytes!(
"../../../harness/experiments/diagram-readability-20260929/mixed-dispatch-original.json"
);
const WARNINGS: &[u8] = include_bytes!(
"../../../harness/experiments/diagram-readability-20260929/mixed-dispatch-expected-diagnostics.json"
);
const RELEASE: &[u8] = include_bytes!("../../../examples/release1/model.json");
const RELEASE_WARNINGS: &[u8] =
include_bytes!("../../../examples/release1/expected-diagnostics.json");
const EIGHT_VIEW: &[u8] = include_bytes!(
"../../../harness/experiments/diagram-readability-20260929/eight-card-view.json"
);
const MAIN: &str = "00000000-0000-0000-0000-0000000186a1";
const EIGHT: &str = "00000000-0038-4000-8000-000000000001";
fn canonical_diagnostics(value: Value) -> Result<Vec<String>> {
let mut rows = value
.as_array()
.ok_or("Expected a diagnostics array")?
.iter()
.map(serde_json::to_string)
.collect::<std::result::Result<Vec<_>, _>>()?;
rows.sort();
Ok(rows)
}
fn admit(bytes: &[u8], baseline: &[u8]) -> Result<Project> {
let project = persistence::decode(bytes)?;
let issues = validation::validate(&project);
if issues
.iter()
.any(|i| i.severity == validation::Severity::Error)
{
return Err(format!("Invalid project: {}", serde_json::to_string(&issues)?).into());
}
if canonical_diagnostics(serde_json::to_value(&issues)?)?
!= canonical_diagnostics(serde_json::from_slice(baseline)?)?
{
return Err(format!(
"Unexpected diagnostics; review without automatically accepting: {}",
serde_json::to_string(&issues)?
)
.into());
}
Ok(project)
}
fn representative() -> Result<Project> {
let mut project = admit(RELEASE, RELEASE_WARNINGS)?;
let view: Diagram = serde_json::from_slice(EIGHT_VIEW)?;
if project.diagrams.insert(view.id, view).is_some() {
return Err("Representative view ID collision".into());
}
admit(&serde_json::to_vec(&project)?, RELEASE_WARNINGS)
}
fn rect(point: Position, origin: Position, size: oa_core::Size) -> bool {
point.x >= origin.x
&& point.x <= origin.x + size.width
&& point.y >= origin.y
&& point.y <= origin.y + size.height
}
fn distance(point: Position, a: Position, b: Position) -> f32 {
let dx = b.x - a.x;
let dy = b.y - a.y;
let length = dx * dx + dy * dy;
let t = if length == 0. {
0.
} else {
(((point.x - a.x) * dx + (point.y - a.y) * dy) / length).clamp(0., 1.)
};
(point.x - a.x - t * dx).hypot(point.y - a.y - t * dy)
}
// Intentionally returns every distinct candidate, with no selected-edge bias,
// UUID winner, click dispatch, viewport clipping, or gesture simulation.
fn candidates(
project: &Project,
view: &projection::ProjectedView,
routed: &routing::RoutedView,
point: Position,
zoom: f32,
) -> Value {
let edges: Vec<_> = routed
.edges
.values()
.filter_map(|edge| {
let segments: Vec<_> = edge
.points
.windows(2)
.enumerate()
.filter_map(|(i, pair)| {
let d = distance(point, pair[0], pair[1]);
(d <= 7. / zoom).then(|| json!({"index":i,"distance":d}))
})
.collect();
(!segments.is_empty()).then(|| json!({"edge":edge.id,"segments":segments}))
})
.collect();
let nodes: Vec<_> = view
.nodes
.iter()
.filter(|n| rect(point, n.position, n.size))
.map(|n| n.id)
.collect();
let labels: Vec<_> = routed
.edges
.values()
.filter(|e| {
!e.label.text.is_empty() && rect(point, e.label.bounds.position, e.label.bounds.size)
})
.map(|e| e.id)
.collect();
let association_labels: Vec<_> = routed
.edges
.values()
.flat_map(|e| {
project
.relations
.get(&e.id)
.into_iter()
.flat_map(move |relation| {
routing::association_end_geometry(relation, e)
.into_iter()
.filter(move |end| {
rect(point, end.label.bounds.position, end.label.bounds.size)
})
.map(move |end| json!({"edge":e.id,"source_end":end.source_end}))
})
})
.collect();
let radius = (10. / zoom).max(10.);
let ports: Vec<_> = view
.ports
.iter()
.filter(|p| {
(p.position.x - point.x).abs() <= radius && (p.position.y - point.y).abs() <= radius
})
.map(|p| json!({"element":p.element,"owner":p.owner}))
.collect();
json!({"world_point":point,"zoom":zoom,"edge_candidates":edges,
"node_occluders":nodes,"label_occluders":labels,"association_label_occluders":association_labels,"port_occluders":ports})
}
fn unobscured(value: &Value) -> bool {
[
"node_occluders",
"label_occluders",
"association_label_occluders",
"port_occluders",
]
.iter()
.all(|key| value[key].as_array().is_some_and(Vec::is_empty))
}
fn snapshot(project: &Project, diagram: Id, point: Position, zoom: f32) -> Result<Value> {
if !zoom.is_finite() || zoom <= 0. || !point.x.is_finite() || !point.y.is_finite() {
return Err("Point must be finite; zoom must be finite and positive".into());
}
let original = project.clone();
let diagram = project.diagrams.get(&diagram).ok_or("Unknown diagram")?;
let view = projection::project_view(project, diagram.id);
let routed = routing::route_diagram(project, diagram, &view);
let mut inventory = Vec::new();
for edge in &view.edges {
let (kind, source, target) = if let Some(r) = project.relations.get(&edge.id) {
(format!("{:?}", r.kind()), r.source(), r.target())
} else if let Some(t) = project.traces.get(&edge.id) {
(format!("Trace:{:?}", t.kind), t.oa, t.sa)
} else {
return Err("Projected edge has no semantic identity".into());
};
inventory.push(
json!({"id":edge.id,"kind":kind,"source":source,"target":target,
"source_port":edge.source_port,"target_port":edge.target_port,
"projected_source":edge.source,"projected_target":edge.target,"trace":edge.trace}),
);
}
inventory.sort_by_key(|row| row["id"].to_string());
let expected: Option<Value> = if diagram.id.to_string() == MAIN {
Some(serde_json::from_slice(include_bytes!(
"../../../harness/experiments/diagram-readability-20260929/mixed-answer-key.json"
))?)
} else if diagram.id.to_string() == EIGHT {
Some(serde_json::from_slice(include_bytes!(
"../../../harness/experiments/diagram-readability-20260929/eight-answer-key.json"
))?)
} else {
None
};
if expected
.as_ref()
.is_some_and(|key| *key != json!(inventory))
{
return Err(format!(
"Projected semantic inventory differs from independently declared answer key: {}",
json!(inventory)
)
.into());
}
let mut edges = Vec::new();
let mut alternate = Vec::new();
for edge in routed.edges.values() {
let projected = view
.edges
.iter()
.find(|p| p.id == edge.id)
.ok_or("Routed edge missing projected identity")?;
let semantic = if let Some(relation) = project.relations.get(&edge.id) {
json!({"relation":relation,"source_element":project.elements.get(&relation.source()),
"target_element":project.elements.get(&relation.target())})
} else {
json!({"trace":project.traces.get(&edge.id)})
};
edges.push(
json!({"id":edge.id,"projected":projected,"semantic":semantic,
"points":edge.points,"label":{"text":edge.label.text,"fraction":edge.label.fraction,
"anchor":edge.label.anchor,"center":edge.label.center,
"position":edge.label.bounds.position,"size":edge.label.bounds.size},
"effective_pins":edge.pins,"pin_fractions":edge.pin_fractions,
"authored_route":diagram.routes.iter().find(|r| r.edge == edge.id),
"custom_suspended":edge.custom_suspended,
"route_warnings":edge.warnings.iter().map(|w| format!("{w:?}")).collect::<Vec<_>>()}),
);
// A positive sample proves a geometric alternate under this query policy.
// Failure to find one does NOT prove global inaccessibility.
let mut line_witness = None;
for pair in edge.points.windows(2) {
for t in [0.25, 0.5, 0.75] {
let p = Position {
x: pair[0].x + (pair[1].x - pair[0].x) * t,
y: pair[0].y + (pair[1].y - pair[0].y) * t,
};
let hits = candidates(project, &view, &routed, p, zoom);
if unobscured(&hits)
&& hits["edge_candidates"]
.as_array()
.is_some_and(|a| a.len() == 1)
{
line_witness = Some(p);
break;
}
}
if line_witness.is_some() {
break;
}
}
let label_hits = candidates(project, &view, &routed, edge.label.center, zoom);
let label_witness = !edge.label.text.is_empty()
&& label_hits["node_occluders"]
.as_array()
.is_some_and(Vec::is_empty)
&& label_hits["port_occluders"]
.as_array()
.is_some_and(Vec::is_empty)
&& label_hits["association_label_occluders"]
.as_array()
.is_some_and(Vec::is_empty)
&& label_hits["label_occluders"]
.as_array()
.is_some_and(|a| a.len() == 1);
alternate.push(json!({"edge":edge.id,"unoccluded_unique_segment_sample":line_witness,
"unoccluded_unique_label_center":label_witness.then_some(edge.label.center),
"negative_result":"No sampled witness is not proof of global inaccessibility",
"native_access":"Not evaluated; Inspector, viewport, selected handles and gesture state excluded"}));
}
if *project != original {
return Err("Projection/routing changed the Project".into());
}
let query = candidates(project, &view, &routed, point, zoom);
let historical_point = Position { x: 1982., y: 375. };
let historical_query = candidates(project, &view, &routed, historical_point, 1.);
let historical_ids = [
"00000000-0000-0000-0000-00000001adb8",
"00000000-0000-0000-0000-00000001adb9",
];
let historical_reproduced = diagram.id.to_string() == MAIN
&& unobscured(&historical_query)
&& historical_query["edge_candidates"]
.as_array()
.is_some_and(|rows| {
rows.len() == 2
&& historical_ids.iter().all(|id| {
rows.iter().any(|r| {
r["edge"] == *id
&& r["segments"]
.as_array()
.is_some_and(|s| s.iter().any(|s| s["distance"] == 0.))
})
})
})
&& historical_ids.iter().all(|id| {
routed
.edges
.get(&id.parse::<Id>().unwrap())
.is_some_and(|e| {
e.points.windows(2).any(|p| {
p[0].x == 1982.
&& p[1].x == 1982.
&& p[0].y.min(p[1].y) <= 336.
&& p[0].y.max(p[1].y) >= 414.
})
})
});
Ok(
json!({"experiment":"issue38 geometry probe; not feature acceptance",
"experiment_declared_base":"715036e132677dd2bc2fb37ed316d93dc9987aec",
"build_provenance":"Require external manifest/source hash verification; this field does not identify the executable build",
"diagram":diagram.id,"project":project,
"project_conserved":true,"projected_view":view,"routed_edges":edges,
"bounds":{"position":routed.bounds.position,"size":routed.bounds.size},
"query":query,"alternate_access_samples":alternate,
"projected_semantic_inventory":inventory,"declared_answer_key":expected,
"historical_witness_reproduced":if diagram.id.to_string()==MAIN {Some(historical_reproduced)} else {None},
"historical_witness_policy":"Exact IDs, zero distance at (1982,375), no occluders, common x1982 span336..414 beyond shared port lead; zoom1. False is a result, not failure.",
"diagnostics":validation::validate(project)}),
)
}
fn write_new(path: &Path, bytes: &[u8]) -> Result<()> {
let mut file = OpenOptions::new().write(true).create_new(true).open(path)?;
file.write_all(bytes)?;
file.sync_all()?;
Ok(())
}
fn main() -> Result<()> {
let args: Vec<_> = std::env::args().skip(1).collect();
if args.len() == 2 && args[0] == "generate" {
// Refuse an existing directory so no previous experiment output is replaced.
let out = Path::new(&args[1]);
let mixed = admit(MIXED, WARNINGS)?;
let eight = representative()?;
let scenes = [
(
"mixed-dispatch",
mixed,
MAIN,
Position { x: 1982., y: 375. },
),
(
"eight-card-representative",
eight,
EIGHT,
Position { x: 600., y: 450. },
),
];
let mut outputs = Vec::new();
for (name, project, id, point) in scenes {
outputs.push((format!("{name}.json"), serde_json::to_vec_pretty(&project)?));
outputs.push((
format!("{name}-geometry.json"),
serde_json::to_vec_pretty(&snapshot(&project, id.parse()?, point, 1.)?)?,
));
}
fs::create_dir(out)?;
write_new(&out.join("mixed-dispatch-original.json"), MIXED)?;
write_new(&out.join("release1-original.json"), RELEASE)?;
for (name, bytes) in outputs {
write_new(&out.join(name), &bytes)?;
}
return Ok(());
}
if args.len() != 8 || args[0] != "probe" {
return Err("Usage: generate NEW_DIRECTORY | probe INPUT_JSON_OR_SINGLE_SOURCE EXPECTED_DIAGNOSTICS DIAGRAM_UUID WORLD_X WORLD_Y ZOOM NEW_OUTPUT_JSON".into());
}
let input = fs::read(&args[1])?;
let project = admit(&input, &fs::read(&args[2])?)?;
let mut result = snapshot(
&project,
args[3].parse()?,
Position {
x: args[4].parse()?,
y: args[5].parse()?,
},
args[6].parse()?,
)?;
result["original_input_utf8"] = json!(std::str::from_utf8(&input)?);
if fs::read(&args[1])? != input {
return Err("Input changed during experiment".into());
}
write_new(Path::new(&args[7]), &serde_json::to_vec_pretty(&result)?)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn representative_preserves_every_original_record_and_view() {
let baseline = admit(RELEASE, RELEASE_WARNINGS).unwrap();
let mut generated = representative().unwrap();
let added = generated.diagrams.remove(&EIGHT.parse().unwrap()).unwrap();
assert_eq!(added.nodes.len(), 8);
assert_eq!(generated, baseline);
let project = representative().unwrap();
let result = snapshot(
&project,
EIGHT.parse().unwrap(),
Position { x: 600., y: 450. },
1.,
)
.unwrap();
assert_eq!(
result["projected_semantic_inventory"]
.as_array()
.unwrap()
.len(),
12
);
assert_eq!(
result["projected_semantic_inventory"],
result["declared_answer_key"]
);
}
#[test]
fn complete_scene_preserves_semantics_sentinel_and_authored_route() {
let project = admit(MIXED, WARNINGS).unwrap();
let before = project.clone();
let id = MAIN.parse().unwrap();
let result = snapshot(&project, id, Position { x: 1982., y: 375. }, 1.).unwrap();
assert_eq!(project, before);
assert_eq!(result["project"], serde_json::to_value(&before).unwrap());
assert_eq!(project.diagrams.len(), 2);
assert_eq!(
result["projected_view"]["nodes"].as_array().unwrap().len(),
10
);
assert_eq!(result["routed_edges"].as_array().unwrap().len(), 14);
for edge in result["routed_edges"].as_array().unwrap() {
let edge_id: Id = serde_json::from_value(edge["id"].clone()).unwrap();
assert_eq!(
edge["semantic"]["relation"],
serde_json::to_value(&before.relations[&edge_id]).unwrap()
);
}
let route = &before.diagrams[&id].routes[0];
assert_eq!(
route.edge.to_string(),
"00000000-0000-0000-0000-00000001adbd"
);
assert_eq!(route.waypoints, vec![Position { x: 300., y: -100. }]);
assert_eq!(route.label.as_ref().unwrap().fraction, 0.4);
assert_eq!(
snapshot(&project, id, Position { x: 1982., y: 375. }, 1.).unwrap(),
result
);
}
#[test]
fn unexpected_diagnostics_and_invalid_source_fail_closed() {
assert!(admit(MIXED, b"[]").is_err());
assert!(admit(b"{}", b"[]").is_err());
let project = admit(MIXED, WARNINGS).unwrap();
assert!(
snapshot(
&project,
MAIN.parse().unwrap(),
Position { x: 0., y: 0. },
0.
)
.is_err()
);
}
#[test]
fn square_port_corner_is_occluded_even_outside_ten_unit_circle() {
let project = admit(MIXED, WARNINGS).unwrap();
let diagram = &project.diagrams[&MAIN.parse().unwrap()];
let view = projection::project_view(&project, diagram.id);
let routed = routing::route_diagram(&project, diagram, &view);
let port = &view.ports[0];
let point = Position {
x: port.position.x + 9.,
y: port.position.y + 9.,
};
assert!(9_f32.hypot(9.) > 10.);
for zoom in [1., 2.] {
let hits = candidates(&project, &view, &routed, point, zoom);
assert!(hits["port_occluders"].as_array().unwrap().iter()
.any(|p| p["element"]==json!(port.element) && p["owner"]==json!(port.owner)));
}
}
#[test]
fn analytical_query_controls_distinguish_edges_segments_and_labels() {
// Synthetic geometry controls using genuine identities, NOT witness evidence.
let project = admit(MIXED, WARNINGS).unwrap();
let diagram = &project.diagrams[&MAIN.parse().unwrap()];
let mut view = projection::project_view(&project, diagram.id);
let mut routed = routing::route_diagram(&project, diagram, &view);
view.nodes.clear();
view.ports.clear();
let first: Id = "00000000-0000-0000-0000-00000001adb8".parse().unwrap();
let second: Id = "00000000-0000-0000-0000-00000001adb9".parse().unwrap();
routed.edges.retain(|id, _| *id == first || *id == second);
for (id, edge) in &mut routed.edges {
edge.points = if *id == first {
vec![
Position { x: -20., y: 0. },
Position { x: 0., y: 0. },
Position { x: 0., y: 20. },
]
} else {
vec![Position { x: -20., y: 50. }, Position { x: 20., y: 50. }]
};
edge.label.text.clear();
}
let point = Position { x: 0., y: 0. };
let hits = candidates(&project, &view, &routed, point, 1.);
assert_eq!(hits["edge_candidates"].as_array().unwrap().len(), 1);
assert_eq!(
hits["edge_candidates"][0]["segments"]
.as_array()
.unwrap()
.len(),
2
);
assert!(unobscured(&hits));
let edge = routed.edges.get_mut(&second).unwrap();
edge.points = vec![Position { x: -20., y: 0. }, Position { x: 20., y: 0. }];
edge.label.bounds = routing::RoutingBounds {
position: Position { x: -5., y: -5. },
size: oa_core::Size {
width: 10.,
height: 10.,
},
};
edge.label.text = "Control label".into();
let hits = candidates(&project, &view, &routed, point, 1.);
assert_eq!(hits["edge_candidates"].as_array().unwrap().len(), 2);
assert_eq!(hits["label_occluders"], json!([second]));
assert!(!unobscured(&hits));
routed.edges.get_mut(&second).unwrap().label.text.clear();
let hits = candidates(&project, &view, &routed, point, 1.);
assert!(hits["label_occluders"].as_array().unwrap().is_empty());
assert!(unobscured(&hits));
}
}
+49
View File
@@ -0,0 +1,49 @@
# Small-diagram reading and editing
Experiment contract for [issue 38](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/38), derived from the user's requirement for intuitive graphical modeling with low cognitive load in [PROJECT_GOAL.md](../PROJECT_GOAL.md). This is a bounded investigation, not acceptance of the application or a routing replacement. Live ownership and dependencies remain in the issue.
## Evidence and uncertainty
The user's image contains eight cards and crowded connection corridors despite substantial free space. A selected path is already highlighted. Correctly selecting an edge therefore cannot, by itself, establish that the reported usability problem is solved.
All displayed names occur in the released `examples/release1` teaching project, but its five saved views do not contain that eight-card arrangement. The original projection, layer identities, positions and authored route overrides are unavailable. Any reconstruction must be labeled representative, with its assumptions stated. It must not be called the user's exact model.
The existing router already penalizes crossings/shared segments and avoids some label collisions. The editor already highlights selected paths/labels and provides route editing. The investigation must compare actual workflows against these existing capabilities, rather than advertise their presence as a new solution.
An independently audited historical full-scene witness from the published #32 corpus has ten nodes and fourteen real exchanges. Two edges share a run beyond a common port lead, without node, label or port occlusion at the sampled point. Both have distinct labels and unshared segments elsewhere. This establishes an ambiguous target at that point, **not a globally inaccessible relation**. Regeneration and input through the current application remain necessary; recorded geometry and a picking-helper test are insufficient.
The [prepared probe, fixtures and independent source review](experiments/diagram-readability-20260929/README.md) make those assumptions executable. Review corrected an initial port-hit radius assumption and a five-edge omission from the representative answer key. The mixed-layer eight-card view has twelve actual relationships and uses Traceability, which flattens visual parents; it is not evidence for nested/folded controls. Current execution and native workflows remain separate gates.
## Alternatives and decision boundary
Criteria are correct relationship identity, useful direct editing, readable paths and labels at working scale, preserved authored intent, predictable latency, and maintainable scope.
| Candidate | Useful outcome | Reason to reject or defer |
| --- | --- | --- |
| Explicit choice at ambiguous connection hits | Deliberately target an overlapping edge without moving the model | Ordinary clicks become cumbersome; selected-edge bend/label editing is intercepted; it merely prints answers while the diagram remains unreadable |
| Local route/label separation | Trace connections without confusing shared runs or detached labels | Authored pins/ports change, unrelated routes regress, occupied area makes Fit unreadable, or expensive rerouting harms interaction |
| Existing focused views | Answer a scoped engineering question with fewer unrelated lines | Hides a required relation or substitutes filtering for the ability to edit the complete small view |
| Selected-label association cue | Associate a displaced label with its highlighted edge | Adds visual noise, conceals nearby content, or fails to address the demonstrated tracing errors |
No implementation candidate is accepted yet. First perform a complete-scene identify/select/repair/undo task and a create-connection task. If targetability is already adequate but tracing fails, prioritize association/separation evidence over additional selection machinery. Any narrow improvement leaves the wider readability gate open until measured directly.
Capella supports local label alignment/reset and a selected edge-to-label attachment cue ([official layout guide](https://github.com/eclipse-capella/capella/blob/master/doc/plugins/org.polarsys.capella.tipsandtricks.doc/html/Tips%20and%20tricks/8_Other%20layout%20facilities/8_Other%20layout%20facilities.mediawiki)). Sirius distinguishes crossing jump marks from label attachment links ([official diagram guide](https://eclipse.dev/sirius/doc/user/diagrams/Diagrams.html)). These provide comparable local repair tools; neither establishes that a chooser or jump mark solves long coincident paths in this application.
## Evaluator
Freeze fixture bytes, source/build, semantic IDs and endpoint answer key, projection, authored routes, camera/window size and an untouched sentinel view before the run. Preserve failed attempts. Include disjoint, crossing, coincident/opposed, near-coincident, shared-endpoint, long/identical-label, fixed-port and nested/folded controls. Do not silently simplify a scene until its failure disappears.
1. **Read:** trace requested relations and identify their endpoints in the complete diagram before showing an answer-bearing Inspector/chooser. Record wrong turns, ambiguous labels/arrowheads, time and recovery actions. Keep this separate from lookup accuracy.
2. **Select:** deliberately reach each competing actual relation at the ambiguous location. Check positive controls at its unique label/unshared segment. Ordinary one-target points stay direct. Selection and cancellation preserve model, history, view and layer. Exercise left click, context targeting and group admission independently.
3. **Draw:** create an intended valid relationship in the populated scene, then identify and edit the new connection. Check refused endpoints and cancellation with no unintended relation or history transaction.
4. **Repair:** move one label, add/move/remove a bend and adjust an eligible attachment. Compare the complete Project against independently declared changes. Semantic endpoints, port ownership, other authored routes and sentinel views remain exact. A gesture commits once; Undo/Redo and save/reopen recover exact authored state.
5. **Recover:** Escape, Help, focus departure, view/projection changes and stale queued input must not commit unintended geometry or retain a held gesture. Failed source/model validation preserves the prior state and redo.
6. **Measure:** report shared-run length per distinct edge pair, nonincident contacts, crossings, node penetrations, label/node/stroke overlap, label association distance, occupied area, Fit scale and action latency separately. Include screen-space near overlaps; world-coordinate separation alone can remain unreadable. Do not hide regressions in one weighted score.
The router deliberately freezes other edges during a route preview, then allows automatic paths/labels to settle on commit. Preserve semantic and authored-field equality; record and assess that visual settlement instead of assuming whole-scene preview equality. Evaluate translation and UUID variants so a favorable identity/order does not become the claimed algorithmic result.
## Implementation boundaries
Ports, nodes, selected attachments/bends/labels and group selection can intercept input before ordinary edge picking. A helper-only change does not cover the production workflow. Any candidates must deduplicate by semantic edge identity, distinguish semantic endpoints from folded visual endpoints, retain captured document/diagram/revision validation, and remain observational until an explicit model or geometry edit.
The existing selected-edge tie preference supports direct dragging; do not replace it with unconditional cycling. A transient ambiguity preview must not create a bend or apply a pending property draft. Coordinate contextual selection with #43 and performance with #44. This investigation does not resume the paused #32 planner or absorb the separate #40 allocation-drag and #41 palette contributions.
@@ -0,0 +1,2 @@
# Preserve checksum-bound fixtures and provenance through Git round trips.
*.json -text whitespace=cr-at-eol
@@ -0,0 +1,105 @@
# Issue 38 bounded geometry experiment
Source preparation for `715036e132677dd2bc2fb37ed316d93dc9987aec`. No compilation,
current-router execution, GUI selection, or feature acceptance is claimed by this
directory. The parent owns execution/publication. Windows only; Linux/WSL excluded.
The Rust example uses `persistence::decode`, `validation::validate`,
`projection::project_view`, and `routing::route_diagram`. It accepts schema JSON or
single-file Arcadia source, not a multi-file source bundle. No production modules
are changed. It records the complete admitted Project, all projected nodes/ports/
edges, semantic endpoint identities, authored and effective route data, settled
geometry, labels, warnings, query occluders, and alternate geometric samples.
Its input source is read-only. Generation preserves original input bytes alongside
normalized complete projects. Individual probe output includes the exact UTF-8
input. Output files use create-new, and generation refuses an existing directory.
## Inputs and independent answer keys
`mixed-dispatch-original.json` is the exact 32,787-byte archived corpus member
`inputs/corpus/mixed_dispatch.json` (see provenance hashes). It has ten function
cards, fourteen Functional Exchanges, two saved views, and authored feedback route
`...01adbd` with waypoint `(300,-100)`, label fraction `.4`, offset `(-30,-70)`.
The secondary view is a conservation sentinel. The original full archive is
linked/hashes recorded in `provenance.json`; it is not duplicated here. Only the
historical **before** geometry from producer `3e21c1bc...` informed the declared
query. The paused boundary-order planner is neither imported nor executed.
`eight-card-view.json` adds one view to the complete, unchanged
`examples/release1/model.json`. Its positions approximate the user's screenshot;
the exact saved user view/camera/pins have not been recovered. The mixed OA Process
and SA cards use an inferred SA/Traceability view, not SA/Overview. This is a
representative reconstruction, not proof of the original projection or identities.
No authored routes are invented. All five original views remain unchanged.
The fixed answer keys were derived from input semantic records and endpoint-owner
placements, independently of the router. `eight-answer-key.json` has **12** edges:
four Allocation, four PortAllocation, two FunctionalExchange, one BoundaryExchange,
and one OA ChainInvolvement. No traces have both endpoints shown. PortAllocation
and BoundaryExchange semantic endpoints are ports; projected endpoints are their
owner placements. `mixed-answer-key.json` has fourteen FunctionalExchanges.
The probe fails if a recognized fixture view's full projected inventory differs,
including kind, semantic endpoints, ports, projected endpoints, and trace flag.
Do not treat the eight-card count alone as a semantic acceptance check.
The explicit archived warning baseline is ten `work_without_behavior_context`
and fourteen `exchange_without_behavior_context` diagnostics. It was prepared
from the input records and coverage rules; first execution must confirm it, not
silently regenerate it from observed diagnostics. The release1 baseline is its
existing one-warning `expected-diagnostics.json`. Every error, missing warning,
extra warning, or changed warning field/message fails admission. No automatic
warning acceptance or migration-based fixture replacement is performed.
## Query policy and limits
The declared historical query is world `(1982,375)`, zoom `1`. Reproduction requires
exactly the two genuine edges `...01adb8` (Actuator dispatch) and `...01adb9`
(Operator dispatch copy), zero segment distance, no node/port/label occluder,
and common vertical x1982 span y336..414 beyond the common port lead. The flag is
false when current routing no longer reproduces this geometry; that is a valid
experiment result, not an implementation failure. The eight-card default query
`(600,450)` is merely a declared inspection point, not an asserted overlap witness.
Line candidates use seven screen pixels converted to world units. Port occlusion
uses the desktop's **axis-aligned square** half-width `max(10/zoom,10)` world units.
It is not a radius-eight circle: `(9,9)` relative to a port center is inside the
square at zoom1 and zoom2 although its Euclidean distance exceeds10. The earlier
local historical audit's radius-eight assumption is superseded; its original
record remains historical and must not be cited as the current port-policy proof.
Node rectangles, nonempty ordinary label rectangles, and association-end label
rectangles are reported independently. Candidates are distinct edge identities,
with every matching segment recorded; a bend does not count as two edges.
Alternate access is a conservative sample at each segment's quarter/middle/three-
quarter points and each label center across the complete scene. A positive result
identifies geometric access under this query policy; a negative result proves
nothing about global accessibility. Native selected handles, gesture state,
viewport clipping, Inspector selection, keyboard behavior, pan/redraw, and actual
input dispatch are excluded. Ordinary labels precede association labels in desktop
dispatch; this probe conservatively rejects either overlap for an unoccluded sample.
It never claims native selection or global inaccessibility.
Read-only Project equality is a capture/conservation control, not an edit/undo test.
Neither automatic route stability after edits nor preview/committed settlement
identity is promised. Native evaluation must separately cover unaided tracing,
assisted selection, creating an intended relation in the populated scene, moving
labels/connections, undo/redo, save/reload, and preservation of authored metadata.
Inherited navigation defects (#43) must be recorded separately from routing (#38).
## Run and review
From the pinned worktree, with a fresh output path:
```powershell
cargo test --locked -p oa-core --example diagram_readability_probe
cargo run --locked -p oa-core --example diagram_readability_probe -- generate .local/issue38-probe-run01
cargo run --locked -p oa-core --example diagram_readability_probe -- probe harness/experiments/diagram-readability-20260929/mixed-dispatch-original.json harness/experiments/diagram-readability-20260929/mixed-dispatch-expected-diagnostics.json 00000000-0000-0000-0000-0000000186a1 1982 375 1 .local/issue38-probe-query01.json
```
Record `git rev-parse HEAD`, local diff, executable hash, Rust version, commands,
stdout/stderr, and output hashes. Verify `provenance.json` source/input SHA256s before
making a pinned-router claim. The output's declared base is not executable identity.
Tests check original-project conservation, full semantic inventories, sentinel and
authored route preservation, deterministic snapshots, failed admission, the port
corner, and analytical distinct-edge/label controls. Reuse current generated
geometry for subsequent native work; do not relabel historical geometry as a new run.
@@ -0,0 +1,134 @@
[
{
"id": "09eaa992-4cdf-5ea3-b748-f10fd74b8cba",
"kind": "PortAllocation",
"source": "e6d69cbd-99d7-596b-bbe3-dada6118a600",
"target": "d2113fb0-a531-54ef-9484-13951b00ab8f",
"source_port": "e6d69cbd-99d7-596b-bbe3-dada6118a600",
"target_port": "d2113fb0-a531-54ef-9484-13951b00ab8f",
"projected_source": "00000000-0038-4000-8000-000000000102",
"projected_target": "00000000-0038-4000-8000-000000000105",
"trace": false
},
{
"id": "3cc9ab52-2b00-57fd-8709-aabc75e5d3e3",
"kind": "Allocation",
"source": "435bf87c-73fd-5e4d-978a-4c471bc9eaf0",
"target": "09342ccb-044b-5483-ac58-87dc3cf7831c",
"source_port": null,
"target_port": null,
"projected_source": "00000000-0038-4000-8000-000000000104",
"projected_target": "00000000-0038-4000-8000-000000000106",
"trace": false
},
{
"id": "55ec4dbf-bd17-5b0c-b3a3-95b2194aa8b9",
"kind": "PortAllocation",
"source": "98d411e7-7c65-5c3f-b543-8bf0f2522ea2",
"target": "24a6850e-b819-57e0-af88-ca4ab76581b7",
"source_port": "98d411e7-7c65-5c3f-b543-8bf0f2522ea2",
"target_port": "24a6850e-b819-57e0-af88-ca4ab76581b7",
"projected_source": "00000000-0038-4000-8000-000000000104",
"projected_target": "00000000-0038-4000-8000-000000000103",
"trace": false
},
{
"id": "568c5c01-de21-52a2-83bd-afdddc158085",
"kind": "PortAllocation",
"source": "98d411e7-7c65-5c3f-b543-8bf0f2522ea2",
"target": "391af7c4-b07d-53a7-8ddd-5c81461fc4f8",
"source_port": "98d411e7-7c65-5c3f-b543-8bf0f2522ea2",
"target_port": "391af7c4-b07d-53a7-8ddd-5c81461fc4f8",
"projected_source": "00000000-0038-4000-8000-000000000104",
"projected_target": "00000000-0038-4000-8000-000000000108",
"trace": false
},
{
"id": "628cb4fc-fe63-5f6b-b364-6268f26b5740",
"kind": "ChainInvolvement",
"source": "022d3cfa-bbaf-5e13-924a-8e55099b8745",
"target": "6a30b763-cc51-546d-907f-5c63657cfd03",
"source_port": null,
"target_port": null,
"projected_source": "00000000-0038-4000-8000-000000000101",
"projected_target": "00000000-0038-4000-8000-000000000107",
"trace": false
},
{
"id": "63009b5f-539e-5914-b630-ec6b7fefb4d5",
"kind": "BoundaryExchange",
"source": "98d411e7-7c65-5c3f-b543-8bf0f2522ea2",
"target": "e6d69cbd-99d7-596b-bbe3-dada6118a600",
"source_port": "98d411e7-7c65-5c3f-b543-8bf0f2522ea2",
"target_port": "e6d69cbd-99d7-596b-bbe3-dada6118a600",
"projected_source": "00000000-0038-4000-8000-000000000104",
"projected_target": "00000000-0038-4000-8000-000000000102",
"trace": false
},
{
"id": "684eadc3-e670-5779-a60f-bbc9a5bfb062",
"kind": "FunctionalExchange",
"source": "9497e10f-04c4-5586-8738-2464a6f00bd4",
"target": "d75eb3fa-a27d-52d5-8782-8b342b3865f6",
"source_port": "994faed7-dbc8-5b96-9d62-7604ac0fbc3a",
"target_port": "391af7c4-b07d-53a7-8ddd-5c81461fc4f8",
"projected_source": "00000000-0038-4000-8000-000000000105",
"projected_target": "00000000-0038-4000-8000-000000000108",
"trace": false
},
{
"id": "725f2e1b-aa2c-5615-9b1f-28da178168fa",
"kind": "Allocation",
"source": "c05b9422-b9da-5de5-b21d-07ad23bd2b8a",
"target": "9497e10f-04c4-5586-8738-2464a6f00bd4",
"source_port": null,
"target_port": null,
"projected_source": "00000000-0038-4000-8000-000000000102",
"projected_target": "00000000-0038-4000-8000-000000000105",
"trace": false
},
{
"id": "93474111-f23e-56c1-b61b-b7c2396b91bd",
"kind": "FunctionalExchange",
"source": "dddae020-a444-5d48-acc8-139d2696a672",
"target": "9497e10f-04c4-5586-8738-2464a6f00bd4",
"source_port": "24a6850e-b819-57e0-af88-ca4ab76581b7",
"target_port": "d2113fb0-a531-54ef-9484-13951b00ab8f",
"projected_source": "00000000-0038-4000-8000-000000000103",
"projected_target": "00000000-0038-4000-8000-000000000105",
"trace": false
},
{
"id": "9b9356d7-2db2-5a94-a47e-ea892cf02afe",
"kind": "Allocation",
"source": "435bf87c-73fd-5e4d-978a-4c471bc9eaf0",
"target": "dddae020-a444-5d48-acc8-139d2696a672",
"source_port": null,
"target_port": null,
"projected_source": "00000000-0038-4000-8000-000000000104",
"projected_target": "00000000-0038-4000-8000-000000000103",
"trace": false
},
{
"id": "b76bf8de-2fa4-563f-a540-dd30f30e6c49",
"kind": "Allocation",
"source": "435bf87c-73fd-5e4d-978a-4c471bc9eaf0",
"target": "d75eb3fa-a27d-52d5-8782-8b342b3865f6",
"source_port": null,
"target_port": null,
"projected_source": "00000000-0038-4000-8000-000000000104",
"projected_target": "00000000-0038-4000-8000-000000000108",
"trace": false
},
{
"id": "e53b7a40-e8a3-5b3f-826d-c2b5c164a84a",
"kind": "PortAllocation",
"source": "e6d69cbd-99d7-596b-bbe3-dada6118a600",
"target": "994faed7-dbc8-5b96-9d62-7604ac0fbc3a",
"source_port": "e6d69cbd-99d7-596b-bbe3-dada6118a600",
"target_port": "994faed7-dbc8-5b96-9d62-7604ac0fbc3a",
"projected_source": "00000000-0038-4000-8000-000000000102",
"projected_target": "00000000-0038-4000-8000-000000000105",
"trace": false
}
]
@@ -0,0 +1,132 @@
{
"id": "00000000-0038-4000-8000-000000000001",
"alias": "issue38_representative_mixed",
"name": "Issue 38 representative eight-card mixed view",
"description": "Research approximation of screenshot positions; not recovered user source. Traceability inferred from OA Process with SA Functions. No authored routes inferred.",
"layer": "Sa",
"projection": "Traceability",
"nodes": {
"00000000-0038-4000-8000-000000000101": {
"id": "00000000-0038-4000-8000-000000000101",
"alias": "issue38_oa_assessment_procedure",
"element": "022d3cfa-bbaf-5e13-924a-8e55099b8745",
"position": {
"x": 314,
"y": 40
},
"size": {
"width": 243,
"height": 101
},
"collapsed": false
},
"00000000-0038-4000-8000-000000000102": {
"id": "00000000-0038-4000-8000-000000000102",
"alias": "issue38_sa_technician",
"element": "c05b9422-b9da-5de5-b21d-07ad23bd2b8a",
"position": {
"x": 1180,
"y": 18
},
"size": {
"width": 265,
"height": 143
},
"collapsed": false
},
"00000000-0038-4000-8000-000000000103": {
"id": "00000000-0038-4000-8000-000000000103",
"alias": "issue38_sa_assess",
"element": "dddae020-a444-5d48-acc8-139d2696a672",
"position": {
"x": 54,
"y": 245
},
"size": {
"width": 265,
"height": 143
},
"collapsed": false
},
"00000000-0038-4000-8000-000000000104": {
"id": "00000000-0038-4000-8000-000000000104",
"alias": "issue38_sa_control",
"element": "435bf87c-73fd-5e4d-978a-4c471bc9eaf0",
"position": {
"x": 736,
"y": 245
},
"size": {
"width": 265,
"height": 143
},
"collapsed": false
},
"00000000-0038-4000-8000-000000000105": {
"id": "00000000-0038-4000-8000-000000000105",
"alias": "issue38_sa_repair",
"element": "9497e10f-04c4-5586-8738-2464a6f00bd4",
"position": {
"x": 1083,
"y": 343
},
"size": {
"width": 266,
"height": 143
},
"collapsed": false
},
"00000000-0038-4000-8000-000000000106": {
"id": "00000000-0038-4000-8000-000000000106",
"alias": "issue38_sa_reset",
"element": "09342ccb-044b-5483-ac58-87dc3cf7831c",
"position": {
"x": 227,
"y": 516
},
"size": {
"width": 266,
"height": 119
},
"collapsed": false
},
"00000000-0038-4000-8000-000000000107": {
"id": "00000000-0038-4000-8000-000000000107",
"alias": "issue38_oa_triage",
"element": "6a30b763-cc51-546d-907f-5c63657cfd03",
"position": {
"x": 1317,
"y": 581
},
"size": {
"width": 243,
"height": 103
},
"collapsed": false
},
"00000000-0038-4000-8000-000000000108": {
"id": "00000000-0038-4000-8000-000000000108",
"alias": "issue38_sa_confirm",
"element": "d75eb3fa-a27d-52d5-8782-8b342b3865f6",
"position": {
"x": 227,
"y": 689
},
"size": {
"width": 266,
"height": 143
},
"collapsed": false
}
},
"routes": [],
"view_options": {
"work_contexts": "All",
"participant_categories": "All",
"coverage": "All",
"hidden_edges": [],
"exchange_labels": "Name",
"work_categories": "All",
"relation_kinds": "All"
}
}
@@ -0,0 +1,156 @@
[
{
"id": "00000000-0000-0000-0000-00000001adb0",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018704",
"target": "00000000-0000-0000-0000-000000018708",
"source_port": "edb626b5-7d99-5015-9faf-71a06232d86e",
"target_port": "ebfcf690-4ac5-5976-864c-337d844b6cd6",
"projected_source": "00000000-0000-0000-0000-000000018705",
"projected_target": "00000000-0000-0000-0000-000000018709",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb1",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018706",
"target": "00000000-0000-0000-0000-000000018708",
"source_port": "d9647cdb-2fcd-5b3f-94b4-ed2a3b8ebf66",
"target_port": "ebfcf690-4ac5-5976-864c-337d844b6cd6",
"projected_source": "00000000-0000-0000-0000-000000018707",
"projected_target": "00000000-0000-0000-0000-000000018709",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb2",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018708",
"target": "00000000-0000-0000-0000-00000001870a",
"source_port": "9244d718-d53a-54cb-a920-5b5312576da6",
"target_port": "14c74a31-2041-59ac-877a-bc8aa0548896",
"projected_source": "00000000-0000-0000-0000-000000018709",
"projected_target": "00000000-0000-0000-0000-00000001870b",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb3",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018708",
"target": "00000000-0000-0000-0000-00000001870c",
"source_port": "9244d718-d53a-54cb-a920-5b5312576da6",
"target_port": "fe1b5efc-983a-593c-a02f-affd40ff5a2f",
"projected_source": "00000000-0000-0000-0000-000000018709",
"projected_target": "00000000-0000-0000-0000-00000001870d",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb4",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-00000001870a",
"target": "00000000-0000-0000-0000-00000001870e",
"source_port": "b4b5d6dc-a2b2-59b0-8898-32dc2e7aa906",
"target_port": "d865e245-0fe6-5c21-a872-d57863a1f857",
"projected_source": "00000000-0000-0000-0000-00000001870b",
"projected_target": "00000000-0000-0000-0000-00000001870f",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb5",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-00000001870c",
"target": "00000000-0000-0000-0000-00000001870e",
"source_port": "f9d7dbf7-a221-5e51-a58f-530d4ca176e7",
"target_port": "51641731-5387-5d58-9bcb-2074acdbe23d",
"projected_source": "00000000-0000-0000-0000-00000001870d",
"projected_target": "00000000-0000-0000-0000-00000001870f",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb6",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-00000001870e",
"target": "00000000-0000-0000-0000-000000018710",
"source_port": "224d8f5f-2a81-5315-b7b6-24fe0cd9e95d",
"target_port": "406e76e5-affd-59a2-bf1b-c3b3b22d3dd2",
"projected_source": "00000000-0000-0000-0000-00000001870f",
"projected_target": "00000000-0000-0000-0000-000000018711",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb7",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018710",
"target": "00000000-0000-0000-0000-000000018712",
"source_port": "8661fe68-51aa-5c8c-827f-e7abea0f6b66",
"target_port": "1c6a0a70-81b3-5c39-a2fd-c3d5b9a112c0",
"projected_source": "00000000-0000-0000-0000-000000018711",
"projected_target": "00000000-0000-0000-0000-000000018713",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb8",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018712",
"target": "00000000-0000-0000-0000-000000018714",
"source_port": "169224b3-b692-540d-ac73-1435e2a87b04",
"target_port": "b1949371-949b-5cb5-acab-485dc4de2e80",
"projected_source": "00000000-0000-0000-0000-000000018713",
"projected_target": "00000000-0000-0000-0000-000000018715",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adb9",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018712",
"target": "00000000-0000-0000-0000-000000018716",
"source_port": "169224b3-b692-540d-ac73-1435e2a87b04",
"target_port": "01abf25b-82c2-51bc-adbc-73cf6f411b2d",
"projected_source": "00000000-0000-0000-0000-000000018713",
"projected_target": "00000000-0000-0000-0000-000000018717",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adba",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018716",
"target": "00000000-0000-0000-0000-000000018714",
"source_port": "bbee14f0-a8d5-5a0d-90e3-01f2d4c5e088",
"target_port": "e9dc0338-4237-5cc0-9e7f-7031b51c4772",
"projected_source": "00000000-0000-0000-0000-000000018717",
"projected_target": "00000000-0000-0000-0000-000000018715",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adbb",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-00000001870c",
"target": "00000000-0000-0000-0000-000000018714",
"source_port": "7d27cb1e-8dc7-5ebe-9407-a1d05e547562",
"target_port": "193bd9b2-e1dd-5764-8f41-777884d3a5ea",
"projected_source": "00000000-0000-0000-0000-00000001870d",
"projected_target": "00000000-0000-0000-0000-000000018715",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adbc",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018706",
"target": "00000000-0000-0000-0000-00000001870c",
"source_port": "2fcb392a-80e8-5be4-990c-79de2be0a19b",
"target_port": "fdf21c35-cd98-5e9a-9a35-faa453f137ca",
"projected_source": "00000000-0000-0000-0000-000000018707",
"projected_target": "00000000-0000-0000-0000-00000001870d",
"trace": false
},
{
"id": "00000000-0000-0000-0000-00000001adbd",
"kind": "FunctionalExchange",
"source": "00000000-0000-0000-0000-000000018710",
"target": "00000000-0000-0000-0000-000000018704",
"source_port": "6f72f5b2-e6c2-5700-a7d5-f945d076fcb8",
"target_port": "e3f59e03-53c7-5d99-97f0-f52542e939c7",
"projected_source": "00000000-0000-0000-0000-000000018711",
"projected_target": "00000000-0000-0000-0000-000000018705",
"trace": false
}
]
@@ -0,0 +1,170 @@
[
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-000000018704",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-000000018706",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-000000018708",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001870a",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001870c",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001870e",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-000000018710",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-000000018712",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-000000018714",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Include this leaf activity/function in a process/chain or scenario through a work action or exchange occurrence.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-000000018716",
"code": "work_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb0",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb1",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb2",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb3",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb4",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb5",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb6",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb7",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb8",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adb9",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adba",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adbb",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adbc",
"code": "exchange_without_behavior_context",
"field": null
},
{
"message": "Use this exchange in a process/chain or scenario.",
"severity": "Warning",
"element": "00000000-0000-0000-0000-00000001adbd",
"code": "exchange_without_behavior_context",
"field": null
}
]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,89 @@
{
"declared_base": "715036e132677dd2bc2fb37ed316d93dc9987aec",
"status": "Source preparation only; no compiled or runtime evidence",
"files": [
{
"path": "crates/oa-core/src/model.rs",
"bytes": 68387,
"sha256": "05a6bd5eef14c77e7cafd2bc72eb3bb6b19f33c6d06184b4e7d21173a2684078"
},
{
"path": "crates/oa-core/src/persistence.rs",
"bytes": 31774,
"sha256": "1c01474f906a63e552c0d590b272c95821c00a100b3e79e58e67dbbeac341a14"
},
{
"path": "crates/oa-core/src/projection.rs",
"bytes": 21687,
"sha256": "3521bf3041502f3a3a970d7799894a5ae16c1e8da754deeea5290ac1f8ba11e8"
},
{
"path": "crates/oa-core/src/routing.rs",
"bytes": 53958,
"sha256": "b377fe80cece3892d64f62ba5e1578d8974eeefc2a317620f343a86f2f257fbd"
},
{
"path": "crates/oa-core/src/routing/attachments.rs",
"bytes": 5918,
"sha256": "cfb4200cdb99e4d86eb6e5ac1b123e4075fb7d4c788860d154365ad8ece139d7"
},
{
"path": "crates/oa-core/src/projection/port_layout.rs",
"bytes": 13474,
"sha256": "a08d17d10a58bb8b206dd001b19b91d5aa3a9f743b298a4da57d503223c20079"
},
{
"path": "crates/oa-core/src/validation.rs",
"bytes": 113436,
"sha256": "f440bfe0f6493e5155eb377eaa766f903abea6006c02325a4b1f09a0b349b8f8"
},
{
"path": "crates/oa-core/src/validation/coverage.rs",
"bytes": 6478,
"sha256": "b08e4b344ecad055714168c83beff7fe29ba4edc53edbffa2eae04e5580a4d5f"
},
{
"path": "crates/oa-desktop/src/diagram.rs",
"bytes": 124738,
"sha256": "3170397b82236d6db2a57a1bb860c3c44fd8ec16172701e6784d4bf1314d140f"
},
{
"path": "examples/release1/model.json",
"bytes": 134520,
"sha256": "ef0ef4006c3e09d5db6e1b621256b3f7d6642f3a7b09cc34607cfbcca830c727"
},
{
"path": "harness/experiments/diagram-readability-20260929/mixed-dispatch-original.json",
"bytes": 32787,
"sha256": "81d25af021cbad18af4262a220ba60372e51ca19083b7e80f2b19196baf05990"
},
{
"path": "harness/experiments/diagram-readability-20260929/eight-card-view.json",
"bytes": 3641,
"sha256": "f851e7e96fc6f5b1edb328b94eb991aa8e94b14cc1bcd07b2c42d32310c4527b"
},
{
"path": "harness/experiments/diagram-readability-20260929/mixed-dispatch-expected-diagnostics.json",
"bytes": 6187,
"sha256": "240033ff6b0664580234e5211521a533d9227c8e250e8afaf3c85e95c485cec1"
},
{
"path": "harness/experiments/diagram-readability-20260929/mixed-answer-key.json",
"bytes": 6683,
"sha256": "1696f06e8a4099c2863ad6a5c444ca41ade0848a183b7cd8311400553a0c3d5f"
},
{
"path": "harness/experiments/diagram-readability-20260929/eight-answer-key.json",
"bytes": 5337,
"sha256": "8968fcc625e8584168ebb6b82f622307bdeb3db4c9c4fdbbc9fbdde3683a6efb"
}
],
"historical_archive_url": "http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/raw/commit/4fcb65c0d3ec7aabf2315bb3f08b64a7cdd2af99/harness/experiments/boundary-order-20260925/evidence/boundary-order-3e21c1bc.zip",
"historical_archive_sha256": "bb6b896ff2401290869a904f7768ebb52b6f0ab36fada06badbe9abb301c37b8",
"historical_producer": "3e21c1bca245944ae0ec7bb02aaa920f45f2cc30",
"historical_geometry": "before only; candidate planner is paused and not used",
"probe_source": {
"path": "crates/oa-core/examples/diagram_readability_probe.rs",
"sha256": "41e2d5b3f170138b87bf30c9b40971c221a62864558294db2be4e61b7b139989"
}
}
@@ -0,0 +1,31 @@
# Independent preparatory review
Read-only review of the new Rust probe, fixed inputs/answer keys, experiment
contract and production projection/picking code. No compiler, probe or GUI was
run by the reviewer. No remaining source blocker was identified before execution.
The author addressed these consequential findings:
- Port-owner mapping adds five valid relationships to the representative view:
its complete inventory is twelve, rather than seven. Kind, semantic endpoints,
ports, projected endpoints and trace identity are checked independently.
- Port occlusion follows the actual square half-width `max(10/zoom, 10)` world
units, with a corner control; association-end label rectangles also precede
ordinary line picking and are included.
- Query candidates count semantic edges separately from their matching segments.
Empty ordinary labels do not occlude a point merely because bounds remain.
- Historical reproduction requires both declared real identities, zero distance,
no occluder and the recorded common span. Declared source provenance requires
actual build/fixture verification before a current-router claim.
Admission, warning baselines and current routing remain execution gates.
Two flat scenes cannot establish nested/folded behavior; Traceability removes
visual parents. Geometric samples do not establish native selection, readability
or global inaccessibility. Read-only Project equality does not verify edit
transactions, Undo/Redo or save/reopen. Drawing a new relation requires a separate
declared post-change answer key and Project delta.
The next workflow should trace, deliberately select, repair one label or bend,
then undo in the complete scene, with unique labels/unshared spans as controls.
Creating a valid relation, refusal and cancellation are separate tasks. A chooser
that prints correct endpoints cannot substitute for unaided diagram readability.