9.4 KiB
Behavior Delete extension design — 2026-09-12
Read-only investigation on codex/behavior-authoring-20260910, clean working tree at inspection. Root owns issue 15 coordination, changes and executable verification. No compilation or runtime claims are made here.
Recommended behavior
Use the existing confirmed semantic deletion path for both Delete and Shift+Delete on persisted process/scenario objects. These projections have no independent diagram placements. The keyboard deletes the selected occurrence, lifeline or persisted link, never the referenced work/performer/exchange/scenario definition. Do not add hidden-occurrence storage. Explain this in the derived interaction contract and shortcut hints. No core command/schema change is needed.
- Process action/exchange/control/sequence-reference node: delete its Occurrence ID. Owned descendants and incident SequenceLink/ContextualLink records follow existing cascade rules. The referenced reusable work, exchange or sequence remains. Existing evidence:
behavior/topology/direct_tests.rs::direct_process_reconnect_delete_and_reorder_preserve_other_paths_and_sourcesalready asserts work/exchange survival and exact bundle undo, but does not exercise keyboard/App. - Process persisted SequenceLink or ContextualLink: delete relation ID; preserve both endpoints. Ordered/implied paths are a projection of ordered steps, not records and must never map to deletion of an endpoint. Hit logic currently uses persisted
edges; preserve this separation. - Scenario message/action/state/scenario-use: delete Occurrence ID. Reusable exchange, action, state or referenced scenario and other uses remain. Deleting an asynchronous/synchronous call may clear reply_to on retained replies according to existing reference cleanup; do not invent additional cascade.
- Scenario fragment: delete its occurrence and owned branches/nested occurrences; never referenced definitions. Branch deletion removes its descendants and prunes the fragment branches list.
- Scenario participant/lifeline: delete Lifeline ID, preserve represented actor/component. Existing semantics retain scenario occurrences and clear affected sender/receiver/lifeline references and scenario-use bindings. This can expose incomplete messages for repair; confirmation/help should describe it accurately rather than promise all incident messages vanish.
MessageStyle::Delete/ palette labelDelete participantis a modeled destruction message, unrelated to editor Delete.- Blank owner/background and non-visible stale selection: no model deletion.
Key risk: qualified process selections
app/process_canvas.rs SelectQualified resolves the endpoint, stores the first reference occurrence in app.selected, and separately stores the qualified ContextAddress. Pressing Delete against app.selected would unexpectedly remove the outer reference when the visibly selected item is an expanded nested definition occurrence. Smallest safe extension refuses keyboard deletion for a live qualified selection, explaining that the user can open the definition to edit it (or select the outer reference itself to remove that use). Do not silently remove the outer reference or the shared definition child. Add explicit test with two uses of the same reusable sequence.
Existing paths and smallest extension
behavior/canvas_workspace/viewport.rs: actual Focusable input owner already blocks child keyboard events while unfocused/disabled, tracks pointer click, resets on window focus loss, and supports F6/Tab viafocus.rs. Reuse it unchanged unless mounted tests identify an actual focus issue. Avoid a global Delete subscription and avoid replacing it with cursor-inside heuristics.- Process
behavior/topology/direct.rs::Graph::input(around 580), scenariobehavior.rs::Scene::update(around 1775): add Delete KeyPressed arms with no repeat, modifiers exactly empty or SHIFT, editable Select tool, no pending press/source, visible selected projected object, and live context. Publish newCanvasEvent::Remove(Context, Id)variants. Child keyboard has already passed viewport focus validation, and therefore must work when pointer moved outside while focus remains. Do not require cursor visibility for keyboard. - Process
topology/direct.rs::CanvasEventand scenarioscenario.rs::CanvasEvent: include Remove in context() matching. Scenario projection has boxes/lines/rows/lanes; process Graph has nodes/edges. Use actual rendered IDs for eligibility and reject owner, synthetic or qualified previews. Do not implement eligibility as onlyproject.contains(id). app/process_canvas.rs::edit_process_canvasand scenario dispatch inapp.rs(around 1659): revalidate exact context equality, selected ID, current tool, clean/modal state, owner membership, and qualified-selection exclusion. Selected_behavior already resolves SequenceLink and ContextualLink ownership and parent chains. Prefer a small reusable behavior target predicate to avoid incompatible UI and App guards.app/diagram_removal.rs: reuse capturedbegin_model_delete(None)after validation, BUT extend Confirmation with derived owner scope (e.g. optional topology/scenario context or common document/owner/revision/epoch snapshot). At confirmation, require the same owner context after the modal's intentional epoch update.model_menu::Openedalready captures document/revision/epoch/page/layer butcurrent_diagram()on Behavior only consults state_context; it does not explicitly witness process/scenario active_owner. An explicit derived-owner stamp closes that gap without pretending an occurrence owner is a placement diagram. Also avoid carrying stale state-diagrambehavior.diagram.placementinto derived behavior confirmations: normalize it to None there.- Existing pointer route is select item then global Actions menu (
model_menu.rsDelete from model -> Message::Delete). Neither process nor scenario canvas currently offers a direct right-click remove handler. Preserve this pointer path; expose Delete / Shift+Delete shortcut hint for eligible derived behavior selection. Menu hint currently requires route/state request.canvas and is blank for derived behavior. Do not fake a route_edit::Context solely for hints. - Core
session.rsCommand::Delete ->references::delete_cascade_deferred; RemoveStep uses the same cascade but index identity is less suitable than captured UUID. Ordinary App execute owns history and source synchronization.references.rs::deletion_impact_innerfollows ownership/dependent references, not outbound reusable targets;remove_objectsprunes ordered steps and clears lifeline/message bindings. Use this authority rather than hand-built GUI deletion commands.
Acceptance examples for root to execute
Mount actual App.view and reuse app/diagram_removal_tests.rs harness. Helper dispatch alone cannot verify focus.
- OA process and SA chain: pointer-select occurrence, release, Delete before redraw; modal names captured occurrence. Cancel/Escape changes no source/history. Confirm removes occurrence and incident authored link while underlying work/exchange and sibling use remain. One Undo restores exact Document bundle/model/source; Redo repeats.
- Persisted link deletion leaves both endpoints, other routes and references. Selecting legacy ordered/implied connector cannot delete an endpoint. Fragment/control/sequence-reference deletion tests preserve referenced definitions.
- Scenario: actual participant header, message line, action/state row, scenario-use frame, nested fragment/branch selection. Parameterize representative target semantics. Confirm actor/exchange/action/state/scenario definition and second use survive. Lifeline deletion specifically checks occurrences retained with cleared endpoints/bindings, and rendered incomplete state is understandable. Fragment removes exactly its owned subtree. Parse generated source and compare with model; Undo restores exact bundle.
- Clean Inspector text reached by actual click, Tab and programmatic focus after canvas selection owns Delete (no modal/message); also source search/editor, scenario form, topology draft, toolbar and palette controls. F6 restores canvas ownership and shortcut. Window Unfocused/resize, page/owner change and open modal retire former ownership.
- Selection/owner/document/revision/camera epoch changes between emitted Remove and update reject it. Confirmation token replay after Cancel, new dialog, Undo, owner switch and selection change cannot remove any other object. Test same selected UUID with changed active_owner explicitly because derived views currently lack owner in model_menu current_diagram.
- Pending creation, message source, reorder drag, connect and reconnect suppress Delete. Test click-release before redraw so retired press cannot consume first shortcut. Unsupported Ctrl/Alt/Super combinations and repeats emit no remove. Selected hidden item/background is inert.
- Qualified process expanded nested child with two references: Delete does not delete outer reference or shared child; fresh selection of actual outer reference permits deletion of that use, preserving reusable sequence and second use. Opening definition permits deletion there with correct owner capture.
This is a UI/spec extension of existing semantic commands. It does not require altering the user-owned specification or semantic storage model. Root should record the accepted modifier/occurrence/lifeline semantics in the established derived interaction contract and carry runtime evidence in the issue/PR.