diff --git a/.gitattributes b/.gitattributes index 81f4fb6d..7f670e88 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,6 +17,7 @@ harness/scenario-group-spacing-evidence-20260924/** -text whitespace=cr-at-eol harness/scenario-reorder-interaction-evidence-20260924/** -text whitespace=cr-at-eol harness/scenario-group-removal-evidence-20260925/** -text whitespace=cr-at-eol harness/scenario-removal-followup-evidence-20260925/** -text whitespace=cr-at-eol +harness/fixtures/process-removal-removed-v2-20260925.arcadia -text whitespace=-blank-at-eol # Exact source/recovery oracles and archived ledgers retain their measured bytes. # Keep ordinary text diffs; only disable checkout/check-in newline conversion. diff --git a/crates/oa-desktop/src/app/process_removal/tests.rs b/crates/oa-desktop/src/app/process_removal/tests.rs index 54a8d22b..b6fb966b 100644 --- a/crates/oa-desktop/src/app/process_removal/tests.rs +++ b/crates/oa-desktop/src/app/process_removal/tests.rs @@ -59,7 +59,9 @@ const BASELINE: &str = include_str!(concat!( )); const REMOVED: &str = include_str!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../harness/fixtures/process-removal-removed-20260925.arcadia" + // V1 assumed canonical whitespace. V2 independently retains removed-token + // trivia and existing reference spelling under language section 7's rules. + "/../../harness/fixtures/process-removal-removed-v2-20260925.arcadia" )); fn native() -> App { let document = Document::from_bundle(native_bundle(BASELINE), Default::default()).unwrap(); diff --git a/crates/oa-desktop/src/model_menu.rs b/crates/oa-desktop/src/model_menu.rs index bf129e93..c11d5c26 100644 --- a/crates/oa-desktop/src/model_menu.rs +++ b/crates/oa-desktop/src/model_menu.rs @@ -436,6 +436,32 @@ pub fn popup(app: &App) -> Option> { )), true, )); + // Navigation belongs to the exact clicked preview, even when it + // is outside the current group. Keep it alongside aggregate removal + // without selecting that preview or exposing singleton mutations. + let qualified_definition = request.process.as_deref().copied().and_then(|context| { + let endpoint = match request.selection_target.as_deref() { + Some(Target::ProcessOccurrence(endpoint)) + if !endpoint.references.is_empty() => + { + Some(endpoint.clone()) + } + Some(Target::ProcessPreviewLink { source, .. }) => Some(source.clone()), + _ => None, + }?; + Some((context, endpoint)) + }); + if let Some((context, endpoint)) = &qualified_definition { + entries.push(Entry::title("Reused contents · read-only in this process")); + entries.push(item( + "Open referenced definition", + Message::TopologyCanvas(crate::behavior::topology::CanvasEvent::OpenQualified( + *context, + endpoint.clone(), + )), + true, + )); + } if let Some(snapshot) = app.multiselect_snapshot().filter(|s| s.targets().len() > 1) && (selected || request.process.is_some()) { @@ -494,34 +520,13 @@ pub fn popup(app: &App) -> Option> { }); } entries.push(Entry::separator()); - if let Some(context) = request.process.as_deref().copied() { - let endpoint = match request.selection_target.as_deref() { - Some(Target::ProcessOccurrence(endpoint)) - if !endpoint.references.is_empty() => - { - Some(endpoint.clone()) - } - Some(Target::ProcessPreviewLink { source, .. }) => Some(source.clone()), - _ => None, - }; - if let Some(endpoint) = endpoint { - entries.push(Entry::title("Reused contents · read-only in this process")); - entries.push(item( - "Open referenced definition", - Message::TopologyCanvas( - crate::behavior::topology::CanvasEvent::OpenQualified( - context, endpoint, - ), - ), - true, - )); - return Some(Menu { - token: opened.token, - anchor: request.anchor, - entries, - on_close: Message::Menu(Event::Close), - }); - } + if qualified_definition.is_some() { + return Some(Menu { + token: opened.token, + anchor: request.anchor, + entries, + on_close: Message::Menu(Event::Close), + }); } } } diff --git a/harness/fixtures/process-removal-removed-v2-20260925.arcadia b/harness/fixtures/process-removal-removed-v2-20260925.arcadia new file mode 100644 index 00000000..f9ff6284 --- /dev/null +++ b/harness/fixtures/process-removal-removed-v2-20260925.arcadia @@ -0,0 +1,34 @@ +arcadia 7; +project process_removal_trial "Process removal trial" id "93600000-0000-4000-8000-000000000001" { + oa_activity intake "Intake" id "93600000-0000-4000-8000-000000000002" { } + oa_activity assess "Assess" id "93600000-0000-4000-8000-000000000003" { } + oa_activity report "Report" id "93600000-0000-4000-8000-000000000004" { } + interaction received "Received" id "93600000-0000-4000-8000-000000000005" { source @intake; target @assess; } + interaction reported "Reported" id "93600000-0000-4000-8000-000000000006" { source @assess; target @report; } + oa_process assessment "01 Assessment" id "93600000-0000-4000-8000-000000000010" { steps [@e1,@e2]; } + + + + + + oa_occurrence e1 "Receive report" id "93600000-0000-4000-8000-000000000012" { parent @assessment; body { Exchange { exchange @received; sender none; receiver none; message_kind Asynchronous; }; }; } + + + + + + oa_occurrence e2 "Forward report" id "93600000-0000-4000-8000-000000000014" { parent @assessment; body { Exchange { exchange @reported; sender none; receiver none; message_kind Asynchronous; }; }; } + + + + + + oa_process caller "02 Response using Assessment" id "93600000-0000-4000-8000-000000000020" { steps [@r1,@r2]; } + oa_occurrence r1 "First assessment" id "93600000-0000-4000-8000-000000000021" { parent @caller; body { SequenceReference { sequence @assessment; }; }; } + oa_occurrence r2 "Second assessment" id "93600000-0000-4000-8000-000000000022" { parent @caller; body { SequenceReference { sequence @assessment; }; }; } + oa_process outer "03 Mission using Response" id "93600000-0000-4000-8000-000000000030" { steps [@r3]; } + oa_occurrence r3 "Response use" id "93600000-0000-4000-8000-000000000031" { parent @outer; body { SequenceReference { sequence @caller; }; }; } + sa_function sentinel "SA preserved function" id "93600000-0000-4000-8000-000000000040" { } + sa_functional_chain sentinel_chain "SA preserved chain" id "93600000-0000-4000-8000-000000000041" { steps [@sentinel_step]; } + sa_occurrence sentinel_step "SA preserved contribution" id "93600000-0000-4000-8000-000000000042" { parent @sentinel_chain; body { Action { action @sentinel; lifeline none; }; }; } +}