# Behavior multi-selection design review — 2026-09-23 Read-only review of published `b8685416f56e53e30bd11ecf93a54cea10ac8458` for issue #29. This note is a local-only proposal until the owning session publishes it. No runtime, build, GUI, or acceptance claim is made. Process, Scenario, and State are all within the requested usability outcome; architecture-only support is not equivalent completion. ## Verified implementation boundaries | Surface | Identity and current gesture | Source at reviewed commit | | --- | --- | --- | | Process | Local occurrence/relation UUID; expanded reusable contents additionally require `OccurrenceEndpoint { references, occurrence }` and owner. Dragging the selected handle changes `steps` order only within one derived graph column. Links determine columns. | `crates/oa-desktop/src/behavior/topology/direct.rs:5`, `:29`, `:112`, `:448`, `:721`; `crates/oa-desktop/src/app/process_canvas.rs:145` | | Scenario | Lifeline/occurrence UUID under a scenario; occurrence rows also identify their immediate sequence/operand parent. Drag changes semantic ordering, constrained to the same parent; the candidate is validated. A stationary row click selects on release. | `crates/oa-desktop/src/behavior.rs:1171`, `:1217`, `:1909`, `:1985`; `crates/oa-desktop/src/behavior/scenario.rs:1091` | | State | Semantic UUID and exact placement UUID are distinct. Vertex movement mutates persisted geometry and includes projected descendants. State-action rows and transitions are relation UUIDs. | `crates/oa-desktop/src/behavior/state_canvas.rs:136`, `:983`, `:1359`; `crates/oa-desktop/src/behavior/state_edit.rs:131`, `:533` | | Navigation | Existing `Surface`/`Target` describes saved placements, state rows, qualified process occurrences, preview links and scenario objects. Its navigation highlight is explicitly not an editing target. Coordinate with #31 before touching its files. | `crates/oa-desktop/src/diagram_usage.rs:105–156` | The shared viewport admits keyboard events only while focused, keyboard-enabled, and outside pan/space gestures (`behavior/canvas_workspace/viewport.rs:327`). `keyboard_owner_enabled` excludes modal menus, confirmation, reference inputs, workflow, and usage chooser (`behavior/canvas_workspace.rs:17`). Dirty inspector/source drafts and pending behavior editors add mutation guards (`app/diagram_removal.rs:24`; `behavior/state_view.rs:178`). Pointer state already captures document, owner/diagram, revision and epoch, and cancels on relevant context/view changes. Preserve these lifetimes. Delete currently has materially different meanings: persisted placement Delete hides that placement; Shift+Delete targets its semantic object. Derived Process/Scenario Delete removes the local semantic occurrence or relation, preserving reusable definitions. Qualified Process preview contents cannot be deleted from the caller (`app/diagram_removal.rs:55–110`, `:170`; `app/process_canvas.rs:96–113`). This distinction must remain visible in any group action. ## Recommended minimal coherent contract (proposal) Use a common selection interaction and family-specific command planners. Do not add free geometry to derived Process/Scenario diagrams as an accidental consequence of this feature: doing so would require a separate persisted-layout specification and source/API parity decision. 1. Selection belongs to `(document identity, surface identity)`, contains typed visual targets in stable order, and has one primary target for the existing inspector. Semantic UUID alone is insufficient for saved placements or repeated Process previews. A selected target may be inspectable without being movable/deletable. Invalid or newly hidden targets are pruned when the document/view changes; navigation to another owner clears selection (Back may deliberately restore a separately captured selection). 2. Plain click an unselected target replaces the set. Shift+click toggles membership; adding makes it primary, removing the primary selects the most recent remaining member. Track modifiers in the focused input path, not through a global shortcut that also fires in text fields. Shift selection must precede endpoint/bend/reorder handles and must never reconnect or start a move. 3. Press on a selected movable target preserves the set so drag moves the group. A stationary plain release may collapse to that target; decide once across families and test it. Right-click a selected member preserves the group; right-click an unselected item makes it the sole member before presenting commands. A stationary background click clears selection. Existing background pan must not clear it merely because the pan started. 4. Paint every member, distinguish the primary, and show a concise count and available group action. Single-object property editing remains explicitly the primary object's editor. Do not imply that changing its name, type, guard, or operand edits the set. 5. Group operations capture the complete immutable target set, primary, document, surface, revision and gesture epoch. Preview and commit use that same snapshot. One successful gesture is one document transaction/undo entry; Escape, stale context, modal entry, tool switch, focus loss, invalid release, or validation failure produces no partial edit. Preserve selection after successful mutation and reconcile after undo/redo. 6. Capability admission evaluates the entire selection, including qualified/read-only members. Unsupported mixtures must give a specific reason; silently acting on only the primary or silently omitting unsupported members is not a group action. If a supported action intentionally affects only nodes in a nodes-and-edges set, label it "Move selected nodes" and derive attached routes; never move a selected edge's endpoints implicitly. ## Family-specific group operations (proposal) **State/saved geometry:** Translate all selected root placements by one world-space delta. Expand the union of each root's descendant placements from the original model; parent plus selected child moves the child exactly once, including folded descendants. Preserve semantic ownership and all relative offsets. Compute route translation against the complete placement-delta union once. `Command::Batch` containing independent `MoveNode` calls is not sufficient: each `MoveNode` currently computes its own route delta map (`oa-core/src/session.rs:771–806`), so a route between two independently moved roots does not receive the same treatment as one whose endpoints move in one operation. Prefer a small shared core group-move command/planner used by both saved architecture and State. Coordinate geometry interaction with #16; group resizing is not implied by group movement. **Process:** Reorder a selected group of local contributions as a stable block within their existing derived column. Retain their prior `steps` order, remove all selected IDs, and insert before the chosen unselected contribution. Drop inside the selected block is a no-op. Require one owner and one column for all selected contributions; reject qualified previews, links, and cross-column sets for this operation. Preserve all sequence/contextual link UUIDs, endpoints and guards. Presentation order must not become new topology. Explicitly distinguish the legacy ordered-path representation, whose `steps` order can carry behavioral meaning; the planner must validate the complete candidate and guidance should say "Reorder contributions", not "Move freely". Add block movement through both pointer and bounded earlier/later controls so small Fit views remain usable. **Scenario:** Reorder selected occurrences as a stable block inside one immediate sequence/operand parent, with the same remove-then-insert algorithm and no-op rule. Lifelines are not occurrence rows and cannot participate in this operation. Nested fragments/branches move with their owned contents; do not separately reorder a selected descendant when its ancestor is moved. Cross-parent selection may remain useful for inspection/deletion but cannot silently migrate between operands. Run existing full-candidate validation once after the proposed order, preserving call/reply, activation, lifecycle, and fragment semantics. Invalid causal order returns an actionable reason and retains the original sequence. Do not claim pixel-distance preservation or arbitrary x/y movement in this temporal view. **Bulk removal across families:** Reuse the current semantics with an aggregate target/impact preview and one atomic transaction. Deduplicate objects and descendants so overlapping selections are not applied twice. For persisted diagrams, expose "Hide selected placements" separately from "Delete selected model objects"; never silently delete an edge semantically in an otherwise placement-only hide action. For Process/Scenario expose "Delete selected occurrences/relations" and preserve definitions. Read-only qualified preview contents require opening the definition; selecting the outer reuse occurrence allows deleting that use. The preview must enumerate actual dependency effects (e.g. deleting a participant clears occurrence participant references; deleting a fragment removes its owned branches/steps) rather than merely displaying a count. Useful later group actions discovered from these workflows are hide/show placement sets, align/distribute geometry-only nodes, reorder block earlier/later, duplicate an occurrence block with identity/reference remapping, and applying genuinely shared editable properties. Alignment is not meaningful for derived temporal/topology layouts. Duplication and batch property editing each need their own typed semantic contract; do not expose existing singleton actions under a misleading multi-selection label. ## Alternatives and staged dependencies - **Uniform free drag everywhere:** visually simple but breaks the present Process topology/Scenario temporal representation or requires new persisted layout models. Reject for this change unless explicitly undertaking that broader semantics work. - **Geometry surfaces only:** quickest, but leaves the user's family-wide inability to select/act on groups unresolved. A staged delivery can name this limitation; it cannot close the full outcome. - **Common selection plus capability planners:** recommended. Reuses transaction, identity and input guards while respecting derived layouts. Costs a small shared selection layer plus independently testable State, Process and Scenario planners. Implementation dependency order: (1) selection identity/primary/context reducer and gestures; (2) union-based core geometry movement and State/saved adapters; independently, (3) stable-block reorder planners and Process/Scenario adapters; (4) aggregate removal with impact/confirmation lifetime; (5) realistic OA/SA native validation, undo/save/reopen/source parity, fresh review. Sharing selection does not require merging navigation-only `Focus` with mutable selection or generalizing every diagram renderer. Choices to settle explicitly: stationary click on an already selected member retains group versus collapses on release; whether Shift+click toggles or only adds (toggle recommended); exact shortcut for Select All when canvas owns focus; whether marquee replaces existing blank-left-drag panning (retain current pan and defer marquee unless a deliberate cross-family gesture decision is made); whether bulk deletion is delivered in the same milestone or left as a specifically tracked gap. None requires changing semantic ownership through a drag. ## High-risk falsifiers / acceptance examples - Select State parent plus child plus an independent node with an edge between roots. Move once: each placement and eligible authored bend translates once; other diagram placements and semantic parents are byte-equivalent. Undo/redo and source round-trip restore exact geometry. - Reuse one subprocess twice and Shift-select the same inner occurrence in both previews. Highlights remain distinct; group deletion/reorder cannot mutate the reusable definition or confuse the two callers. - Select noncontiguous Process contributions in one column, reorder together, and compare every link and guard. Cross-column drop rejects with no partial change. Exercise a legacy exchange-only ordered process as well. - Select Scenario call/reply together and reorder; also move reply alone before call, across an operand, or across create/delete lifecycle boundaries. Valid block succeeds once; invalid candidate leaves sequence/model/source/history unchanged. Fragment plus descendant never duplicates or reparents a step. - Shift-click selected transition endpoint, bend handle, State action row, Scenario message line, and Process reorder handle. Membership changes without starting reconnect/bend/reorder gestures. An ordinary drag still performs the established operation when selection is singular. - Focus inspector text, open modal/usage chooser, switch document with same project UUID, change owner, scroll/zoom, lose window focus, or start source edits during group gesture. No queued release/Delete can commit against stale context; typed text shortcuts remain local. - Select mixed nodes/edges or local/qualified-preview items. UI tells the truth about action capability and never silently operates on the primary alone. All visible selection states remain distinguishable without hue alone. No code edits were made as part of this review. These cases are proposed evaluators, not passing test results.