Preserve the full-desktop-1 captured source. Qualified previews inspect their exact subject read-only; group movement preserves source order and other columns. Native acceptance and strict-lint payload-size corrections follow separately.
125 lines
20 KiB
Markdown
125 lines
20 KiB
Markdown
# Process selection and column-order adapter
|
|
|
|
**Accepted implementation contract; runtime/native acceptance pending.** This document describes the current unpublished Process working source above `bed7f825fd00ec32299032a664a5f5d753aad86d`, implementing the Process portion of the [multiple-selection contract](multiselect-contract-20260923.md). It does not establish a verified release or completion of issue #29. Gitea holds live ownership, priorities and gate results. The initial read-only investigation inspected State follow-up source above `7e348d967d1787c052a145b7448636613a6a7054`; its alternatives, critiques and experiment evidence are retained as history below.
|
|
|
|
## Accepted implemented contract
|
|
|
|
The surface is `Surface::Process(owner)` for an OA Process or SA FunctionalChain. Selection is a transient insertion-ordered exact target set with one primary Inspector target:
|
|
|
|
| Target | Identity and admitted use |
|
|
| --- | --- |
|
|
| `ProcessOccurrence(OccurrenceEndpoint { references: [], occurrence })` | One local semantic contribution card. Action, Exchange, Control and SequenceReference cards participate in order planning; these are not saved placements or reusable work definitions. |
|
|
| `ProcessOccurrence` with nonempty references | One visible qualified tile with its complete reuse path. Select/inspect; no caller-side reorder or deletion. |
|
|
| `ProcessLink(id)` | One local rendered SequenceLink or ContextualLink. Select/inspect; not a member of a contribution reorder. Existing singleton local SequenceLink reconnect remains separate. |
|
|
| `ProcessPreviewLink { relation, source, target }` | One actually painted qualified preview segment with exact endpoint paths. Select/inspect; read-only through the caller. |
|
|
|
|
Different caller paths to the same defining occurrence remain different targets. A qualified primary identifies the **actual occurrence or relation subject**, not the outer reference's properties. Its caller owner/path remains captured; properties are read-only until explicit **Open** navigates to the defining process. Hidden/collapsed targets are pruned exactly without rebinding to another reuse path or outer card. Open/Back and owner changes establish a new surface lifetime. Legacy `OrderedPath` arrows have no relation identity and do not become editable links. Navigation emphasis alone is not mutation authority.
|
|
|
|
Shift-click toggles the exact hit. Plain press on an unselected target replaces the set. A selected-member press freezes membership and primary until stationary release versus drag; stationary release collapses to that member. Right-click captures a target without replacing the selection; contextual Add/Remove changes membership explicitly. Counts and distinct primary/membership paint describe the set. Ordinary local property edits remain primary-only.
|
|
|
|
### One admitted column-slot plan
|
|
|
|
`behavior/topology/group_reorder.rs::Prepared::new(&Project, owner, &[Target])` captures immutable steps, adjacency, integer ranks and selected IDs. It rejects empty/duplicate selections, non-occurrence members, qualified paths, wrong-owner/missing contributions, cross-column roots, or any missing/foreign/duplicate **unselected** step. A local SequenceLink whose endpoint is omitted from owner steps also refuses before any selected-anchor no-op. Such incomplete source can be core-valid; that does not authorize silently ignoring its topology when editing order.
|
|
|
|
The renderer and planner share `topology.rs::column_ranks`, extracted from the established rank algorithm. The renderer filters direct links to displayed endpoints and skips unavailable legacy path pairs, retaining displayed nodes without panicking. This tolerant display behavior does not weaken planner admission. Parallel SequenceLinks remain distinct; longest-path and deterministic cycle-blocked integer ranks retain their existing meaning.
|
|
|
|
`Prepared::plan(Destination::Before(id) | Destination::After(id))` requires an anchor in the selected column. After full admission, a selected anchor returns `Plan::Noop`, even for a noncontiguous set. For an unselected anchor: extract the column's order and original step indices; remove selected IDs and form their block in original source order; insert before/after the remaining anchor; write the column permutation back to those same indices. Other-column IDs retain exact indices, and unselected IDs within this column retain relative order. Identical steps return Noop. Otherwise the complete ID-to-rank map is recomputed and must remain exact before one existing `Command::SetSteps` is returned.
|
|
|
|
Example: `[a,u,b,v,c]`, column `[a,b,c]`, selection C then A, After B yields `[b,u,a,v,c]`. U/V retain indices 1/3, and A/C retain source order. Selected `{b,c}` After A is unchanged. There is no XY schema, control-flow rewiring, reusable-definition edit or unrelated-record change. Legacy exchange-only paths have successive ranks; cycle-blocked nodes receive distinct fallback ranks, so multi-member sets spanning those columns refuse. A cycle elsewhere does not prevent movement of an acyclic peer group.
|
|
|
|
Semantic facts are prepared once per press and candidate plans are cached by anchor/side. Motion within a slot does not clone the Project, rebuild a Graph or run whole-model validation. App release handling re-admits the live exact selection and uses the same planner before one source-validated transaction. Source failure preserves model/history. Successful commit restores the exact primary without an incidental Fit/reveal. Noop emits no command/history entry.
|
|
|
|
**Singleton migration is implemented:** `topology/direct.rs::reorder` delegates a singleton typed target to this planner and returns Noop/Change. Moving B before A in `[a,u,b,v,c]` now writes `[b,u,a,v,c]`, replacing the old global splice `[b,a,u,v,c]`. This deliberately changes invisible cross-column source order to preserve unrelated indices consistently for singleton and group operations.
|
|
|
|
### Pointer feedback and lifetime
|
|
|
|
`behavior/topology/direct_selection.rs` owns an order-specific press; it does not use the saved-placement XY constructor. The press captures exact selection snapshot, target, screen position, bounds, pan and scale. Its 3-screen-pixel threshold is irreversible: out-and-back movement, refused drops and no-op destinations retain the set rather than becoming stationary clicks. Native pointer order is preserved; an unavailable/outside release cannot reuse an earlier inside point.
|
|
|
|
Each local card's upper half means Before and its lower half means After. An insertion indicator, refusal/unchanged caption and source-order member numbers describe the cached operation. Cards are not freely translated or reflowed during the drag. Before-first and after-last are reachable through the first/last unselected card. Actual reachability/readability at native zoom/window sizes remains an acceptance gate.
|
|
|
|
Snapshot/document/revision/surface/selection-generation changes, tool/editability changes, bounds/pan/scale changes, resize/unfocus, wheel, cursor exit, right/middle press and Escape cancel the captured gesture. Escape cancels a live press before selection clearing. Passive modifiers support Shift across editor focus. Stable count/Inspector layout and retained camera geometry are required; selection and qualified inspection do not authorize incidental source application or camera movement.
|
|
|
|
Shift/context hits on painted reconnect rings resolve the exact link. A mixed set cannot reconnect only its primary, and Connect with several selected targets is refused. Existing singleton tools retain their separate ownership. App envelope validation precedes incidental selection/camera/allocation state changes; dirty source/property/forms or stale packets cannot partially act.
|
|
|
|
### Current removal boundary and remaining adapters
|
|
|
|
Process has no saved placements: Hide explicitly refuses instead of deleting contributions or adding hidden-card persistence. Group Delete and qualified-target Delete explicitly refuse. Existing admissible local singleton deletion remains separate; no depicted group falls through to it. Aggregate local-occurrence removal still requires one captured impact review and atomic union deletion, including ContextualLink cleanup and explicit-to-legacy meaning changes described in the historical critique below.
|
|
|
|
This slice implements exact Process selection and column reorder with whole-set refusals. Aggregate removal, Allocation assignment/Arrange-only groups and Scenario order adapters remain required by the [overall contract](multiselect-contract-20260923.md), outside this Process acceptance claim.
|
|
|
|
### Implementation and verification boundaries
|
|
|
|
| Source | Responsibility |
|
|
| --- | --- |
|
|
| `app/multiselect.rs`, `app/process_multiselect.rs` | Scope/envelope admission, exact reconciliation, actual-subject primary and transaction dispatch. |
|
|
| `app/process_canvas.rs`, `app.rs` | Legacy command guards, navigation/tool lifetime and existing Process command integration. |
|
|
| `behavior/topology/group_reorder.rs`, `topology.rs::column_ranks` | Immutable admitted facts, column-slot planning and shared production ranks. |
|
|
| `behavior/topology/direct_selection.rs`, `direct.rs` | Exact hits/modifiers, order gesture, feedback, singleton migration and separate tool ownership. |
|
|
| `behavior/topology.rs`, `topology/view.rs`, `topology/usages.rs` | Exact paint/visibility, stable controls/camera and incomplete-topology rendering. |
|
|
| `model_menu.rs`, Inspector/behavior property boundaries | Captured membership menus, qualified read-only subject and explicit Open. |
|
|
|
|
Planner tests define independent literal OA/SA orders, whole-Project preservation, source roundtrip/Undo/Redo, all four card kinds/differing heights, reverse noncontiguous selection, end gaps/noops, typed refusal, five-node cycle-plus-peer ranks, parallel links and malformed/unlisted steps. App/input tests cover qualified primary, reconciliation, stale messages and gesture boundaries. Their definitions and source inspection alone do not establish current full-suite or native acceptance.
|
|
|
|
The [native evaluator design](multiselect-process-native-design-20260923.md) and [core example](../crates/oa-core/examples/process_multiselect_native.rs) use an immutable deterministic OA/SA fixture, literal expected `[b,u,a,v,c]` and complete Project comparison. Global splice, reversed-block, primary-only and unrelated-record corruptions are falsifiers. Native Shift/focus, menus, cancellation, insertion reachability, stable layout/camera, real save/reopen and Undo/Redo require matching executable evidence. No acceptance receipt is asserted here.
|
|
|
|
## Historical investigation and design alternatives
|
|
|
|
The following sections retain the original reasoning, corrected by the accepted contract above. Statements of proposed work describe that earlier investigation, not the current implementation status.
|
|
|
|
### Representation facts recorded initially
|
|
|
|
`diagram_usage.rs` already defines `Surface::Process(owner)`, exact `ProcessOccurrence(OccurrenceEndpoint { references, occurrence })`, local `ProcessLink`, and qualified `ProcessPreviewLink` targets. Local cards are semantic occurrences in owner steps; they reference reusable definitions and are not saved diagram placements. Different reference paths to the same defining occurrence are different selected targets. Legacy derived `OrderedPath` arrows lack relation identities: do not fabricate editable IDs or substitute endpoints.
|
|
|
|
`topology/usages.rs::Graph::usage_bounds` provides exact displayed-target visibility, including bounded qualified previews. Hidden preview tiles do not fall back to their outer reference. Its approximate reveal rectangles are not adequate edge hit tests. Qualified preview content stays read-only until its definition is explicitly opened.
|
|
|
|
### Movement candidates considered
|
|
|
|
| Candidate | Assessment |
|
|
| --- | --- |
|
|
| Persist arbitrary XY | Conflicts with derived process topology and needs new schema; reject for this adapter. |
|
|
| Sequential singleton reorder | Produces click-order-dependent layouts and does not define a simultaneous operation; reject. |
|
|
| Stable block within one original column | Accepted with the column-slot correction: preserves topology and uses one existing `SetSteps` command. |
|
|
| Rewire links during a cross-column drag | Alters process execution meaning; requires a separate capability and explicit interaction. |
|
|
|
|
The former `topology/direct.rs::reorder` verified local contributions and equal graph X-columns, globally spliced steps, then validated `SetSteps`. `Graph::expanded_qualified` derived ranks from sequence links and legacy order, with steps supplying vertical order; cyclic fallback could depend on enumeration order. The shared integer rank helper and singleton migration supersede that implementation.
|
|
|
|
The proposed pure planner admits the whole nonempty set only if every target is a visible local occurrence with an empty reference path and belongs to this owner. Selected IDs and the destination must share one original column. Build the block in original steps order, then use the column-slot permutation below: preserve other-column indices and unselected order within this column. Object-anchored drops on selected items are no-op; a raw gap is a separate API. Check the complete rank map remains unchanged, especially cycle fallback. Distinguish no-op from a command so it creates no source/history entry. Preview and commit must consume this same planned order; do not freely translate cards.
|
|
|
|
Before-card only could reuse earlier behavior but made moving to the end awkward. Before/After and all four local contribution kinds were subsequently accepted and implemented; actual interaction acceptance remains pending.
|
|
|
|
### Original Hide and removal proposal
|
|
|
|
Process has no saved placements and therefore no existing Hide operation. Explain that limitation; do not silently substitute semantic deletion or add persisted hidden cards without a separate design decision.
|
|
|
|
Homogeneous local-occurrence removal should present one aggregate impact review and apply one `Batch(Delete(...))`. Core deferred deletion already supports union cleanup. Preserve referenced work, exchanges, reused process definitions and opposite-layer counterparts. Initially refuse qualified or mixed sets in full; local-link-only deletion can be a separate extension. Never invoke singleton deletion once per member. Capture document, revision, surface and selection generation through confirmation, revalidate the whole set, and preserve pending property/source/forms.
|
|
|
|
### Initial implementation plan and falsifiers
|
|
|
|
The initial plan called for Process dispatch, exact primary paths, right-click membership and whole-set guards rather than assuming every context is a saved diagram. These are implemented across the source boundaries above; the pure planner and input adapter were factored into dedicated modules rather than expanding `direct.rs`. Aggregate confirmation at `app/diagram_removal.rs` remains deferred.
|
|
|
|
The saved-view group gesture requires placements and an XY anchor. A Process order-specific gesture was chosen, preserving its lifecycle principles without widening that constructor into an inappropriate generic geometry abstraction.
|
|
|
|
First useful outcome: exact local/qualified/link selection with whole-set refusals, plus one atomic same-column local-occurrence reorder and one Undo/Redo. Aggregate removal remains separate acceptance if omitted from that outcome.
|
|
|
|
Tests must independently assert source-order blocks from noncontiguous reverse-click selections; unchanged other-column indices and unselected order within the affected column, relations and reusable definitions; identical referenced work represented by distinct occurrences; distinct qualified paths and collapse visibility; full refusal of cross-column/mixed/stale sets; no-op history; ordered pointer/focus/Shift/cancellation/camera behavior; no primary-only reconnect bypass; OA and SA, legacy exchange order, explicit branches and cyclic fallback. Native slot feedback, stable canvas/camera, source save/reopen and one-step history are required beyond compilation.
|
|
|
|
### Independent critiques that changed the design
|
|
|
|
A fresh read-only Astra review found a concrete defect in global block splicing. Given steps `[a,u,b,v,c]`, one displayed column `[a,b,c]`, and selected `{b,c}`, dropping after `a` is visually a no-op. The proposed global splice produces `[a,b,c,u,v]`, changing source/history without a visible change. Equivalent before/after positions in the same displayed gap must have one interpretation.
|
|
|
|
The correction was a **column-slot permutation**, now implemented as specified above. It deliberately replaces the earlier all-unselected-global-order condition with exact other-column indices and within-column unselected order. The independent pure experiment preceded UI implementation; selected-anchor no-op remained distinct from raw-gap normalization.
|
|
|
|
Scope correction: legacy ordered-path nodes have successive ranks, and cycle-blocked nodes receive distinct fallback ranks (`topology.rs` rank computation). A multiple selection of those nodes cannot satisfy same-column admission. Test refusal and unchanged execution meaning rather than claiming useful reordering of those sets. A graph containing a cycle can still admit a separate acyclic peer group.
|
|
|
|
Removal has an additional semantic consequence: `behavior.rs` derives explicit-versus-legacy interpretation from remaining occurrence kinds and SequenceLinks. Deleting action/control from `[action,exchange1,control,exchange2]` without links leaves an exchange-only sequence with implicit ordered-linear meaning. Aggregate review must expose this transition or initially refuse it. Include caller-owned ContextualLinks targeting removed occurrences in the impact union (`references.rs` cleanup), not just local links. See the presentation-versus-legacy distinction in [language documentation](../docs/language.rst).
|
|
|
|
The original production-evaluator plan became the Rust/App tests and native fixture above. Deletion-induced mode switching still needs a separate removal evaluator. A later source critic found an incomplete-order panic (`a→u` with U omitted from owner steps), prompting complete planner admission and graceful renderer omission. Bounded test results must remain tied to the source and receipts actually exercised.
|
|
|
|
## Bounded permutation experiment
|
|
|
|
[Executable exploratory model and results](experiments/process-column-slots-20260923/README.md) isolate the order transformation from production architecture. An independent Astra investigator implemented it; the parent read both scripts and reran them successfully. The experiment covers 214,990 column cases and 861,096 plans against a four-node graph rank model, with no invariant failures. Its modeled ranks and sentinel serialization do not execute the Rust layout, source writer or history.
|
|
|
|
An object-anchored Before/After interface can keep selected-target no-op without losing changed destinations: all remainder gaps were reachable using unselected anchors in 2,036 bounded subsets. Raw-gap normalization is different: in `[a,b,c,d]`, selecting `{b,d}` and normalizing Before `d` gives `[a,c,b,d]`; selected-target no-op keeps the original, while After unselected `c` reaches the changed order. This supports the simpler selected-target no-op interaction. Block contiguity applies to an admitted planned move, not to that no-op. Actual pointer feedback, production graph admission, serialization and history remain unverified.
|
|
|
|
An independent critic matched the experiment's rank algorithm to Rust and found no counterexample under the stated valid-input assumptions. The review prompted an explicit five-node changed group beside a cycle; four-node enumeration could only cover singleton movement or unchanged whole-peer blocks there. It also identified the singleton global-splice compatibility choice. Retaining that splice was rejected as two source transformation rules for the same visible operation; the implemented migration above intentionally preserves unrelated column indices instead. Native anchor reachability and selected-target guard execution remain separate acceptance checks. These exploratory results support the design but do not replace runtime/native verification.
|