# #32 Auto-layout source and evaluator map Read-only inspection of `.local/auto-layout-20260925`, base715036e132677dd2bc2fb37ed316d93dc9987aec, on2026-09-25. Issue32 explicitly asks automatic creation/removal of bends, attention to connection intersections and labels. No source changes, compiler, GUI, model helper or Gitea writes were performed. No geometric outcomes below are claimed as measured results. ## Actual surfaces and shared execution - `crates/oa-desktop/src/views.rs:443`: saved Architecture diagram toolbar **Auto-layout** sends `Message::Command(Command::Layout(id))`. `model_menu.rs:417`: background **Arrange elements** sends the same command. The generic chooser excludes Scenario, not State, but normal State navigation routes to its dedicated workspace. `behavior/state_menu.rs` returns early for the dedicated State menu, and `state_view.rs` has no equivalent Auto-layout button. State view creation still calls shared Layout (`behavior/state_edit.rs:361–410`); behavior creation batches Layout too (`behavior/creation.rs:477–504`). Distinguish exposed UI from callable core behavior. - `crates/oa-core/src/model.rs:1431`: saved projection kinds are Overview, Decomposition, Allocation, Traceability, FunctionalDataflow, Capabilities, StateMachine and Scenario. `session.rs:947` accepts the first seven; Scenario explicitly refuses and requires its separate geometry operation. Process topology and Scenario lane/message arrangement are separate behavior canvases, not this generic command. - Allocation **Arrange next drag / Alt+A** is a different manual translation workflow (`allocation_drag/arrange.rs`, `app/allocation_drag.rs`, `diagram/work_drag.rs`). Do not silently turn it into automatic graph layout or change its allocation/group/refusal/Undo contracts merely because its label contains Arrange. - `app.rs:3559` applies pending property edits and remaps a changed diagram identity before executing Layout; preserve that admission/identity behavior (`tests.rs::placement_identity_edit_then_drag_and_diagram_identity_then_arrange`). `document.rs:322` prepares/validates a candidate, updates the source bundle with identity provenance, then commits one model/source history entry. Core Session execute at`session.rs:275` likewise records one changed candidate; Undo/Redo swap exact Projects and no-op candidates create no history. - REST transactions at`crates/oa-server/src/routes.rs:1011–1041` accept serialized Command batches including Layout under expected-revision admission, with dry-run and atomic PreparedProject/store commit. Core behavior therefore cannot rely on desktop-only fonts/state. `docs/v2-interface.md` lists Layout convenience commands. CLI exposes check/lint/stabilize/rename-alias, not a direct layout verb. Text/source may store explicit node/route records; parsing/loading them must not implicitly auto-arrange. ## Current algorithm and why the issue is real `session.rs::layout` measures a projection-parent forest, vertically stacks children below headers, and puts roots into rows of four. Roots use an exchange-aware topological-like ordering with alias/ID tie breaks and deterministic cycle fallback. Child order is placement-ID order. It changes node positions/sizes, computes per-node deltas and calls `projection::translated_routes`; it neither scores routed geometry/labels nor generates or removes stored waypoints. `projection.rs:81` supplies actual visual parents: nearest placed semantic ancestor; Allocation derives performer grouping from allocation summaries; Traceability is flat; State filters the machine. Ports have owner placements, not independent node placements (`edge_endpoint_placements`). Measurement reserves minimum sizes, State action/internal rows and port footer (`session.rs:967–1006`). Folded descendants still belong to the authored forest; projected visibility and stored layout are distinct. The router is **not unaware of crossings or labels**: `routing.rs:457` routes stable edge IDs with a bounded search and previous-edge costs; `crossing_cost:990` penalizes proper crossings and positive collinear overlap separately. All paths settle before automatic label placement; `label_geometry`/`label_collisions:1370–1552` consider node obstacles, earlier labels and all visible strokes. But routing remains greedy, has finite search budgets, and authored label placement bypasses avoidance. Auto-layout never feeds these costs back into node order/spacing. Existing pins constrain paths even after node arrangement, except folded/promoted routes temporarily suspend their custom geometry. Thus a node-only Layout plus pinned greedy routing cannot promise the joint improvement requested by32. Base715 uses scalar-estimated label rectangles. The separate #36 measured State multiline layout is not present in this base; do not assume its geometry or acceptance has already integrated. Ordinary labels, association-end labels/stereotypes and State labels also differ in presentation. Parent should coordinate routing-file overlap with #36 before integration, not silently reimplement its measurement policy. ## Contracts requiring an intentional change 1. `harness/release-routing-contract.md:143–146` explicitly says Layout preserves pins, translating only equally moved endpoint owners, with Reset routing separate. `crates/oa-desktop/src/guide.rs:360` repeats **Auto-layout preserves pins**. These are directly incompatible with the user's requested automatic bend removal/generation and must be revised for the explicit whole-view operation, with Undo restoring prior authorship. 2. `crates/oa-core/tests/release_routing.rs::layout_preserves_authored_route_count_order_labels_and_undo` fixes route list count/order, two pins on each route and exact labels. Replace only the superseded Layout assertions with the new declared output contract plus stronger geometry checks; retain unrelated-diagram exactness and exact Undo. Do not remove all route-preservation tests. 3. `state_diagrams.rs::layout_reserves_internal_rows_and_state_routes_retain_hard_pins` combines two things: the **pre-Layout** router must traverse authored pins without mutation; the post-Layout assertions reserve State rows and source roundtrip. Preserve those valid assertions, and rename/split the test if Layout no longer retains pins. Its current body does not actually assert post-Layout pin preservation. 4. Decide explicitly whether generated bends remain derived route corners or become persisted editable waypoints. The user asks visible automatic routing improvement, not necessarily a new semantic field. `routing.rs` documents only authored pins in the model; blindly storing every solver corner would turn generated geometry into hard constraints and can cause second-run drift. Whichever representation is chosen needs first/repeat Arrange and manual-after-Arrange tests. 5. Define scope for invisible/suspended routes, authored label offsets and explicit attachments. User32 authorizes changing bends; it does not automatically authorize deleting manual semantic-port metadata, every hidden route, or authored label placement. A narrow baseline is reset/rebuild bends for eligible visible edges while retaining attachments, labels and unrelated/hidden records; measure whether that suffices. If an authored label constraint defeats label clearance, report it or make its intentional reset policy explicit instead of pretending the layout is collision-free. ## Invariants to retain Complete elements/relations/traces/types/names/text/IDs/containment/allocations/occurrences remain exact; only the selected diagram's declared presentation fields may differ. Other diagrams, node identities, collapsed flags, view options, unplaced elements and manual port side/fraction metadata remain exact. Automatic port geometry may change with node geometry, but semantic port identity/contract and explicit anchors do not. Existing tests `port_anchors::separate_views_resize_layout_and_hidden_owners_keep_manual_metadata`, `workflows` nested-parent containment, and desktop port/identity tests are relevant sentinels. Keep move-one-end pins-fixed and move-both-owners equal-delta translation in `projection::translated_routes`, normal drag/reflow/group previews, reconnect, folding suspension/resumption, Hide/show and Reset routing unchanged unless separately justified. Keep finite geometry and existing point-count/attachment/fraction validation (`validation.rs:650–720`), deterministic bounded failure/fallback, source/JSON roundtrips, dry-run nonmutation, rejected-operation no-history, and exact one-step Undo/Redo. A Layout improvement must not trigger new allocation/reconnect semantics. ## Independent fixtures and falsifiers to freeze before candidate comparison Use hand-declared topology, IDs and geometry; record the old candidate's metrics once, then compare materially different candidates against the same evaluator. Do not use a candidate's own cost function as the acceptance oracle. | Fixture | Independent observable requirement | | --- | --- | | Two connected OA activities / SA functions with redundant dogleg and one blocking third card | Explicit Arrange removes obsolete turns; unobstructed edge straightens, blocked edge gains necessary orthogonal turns; semantic endpoints exact, no nonincident-node penetration. Include authored source/target attachments. | | Two containers, each with two children, reversed child-order connections | A planar hand-constructed witness has zero proper crossings; require zero on this bounded case, child containment and preserved port contract. Detect current ID-ordered child stacking weakness. | | Four-node diamond plus return edge, self-loop, parallel and reverse-direction edges | No false cycle/deadlock; meaningful separation of parallel paths; self-loop remains visible; distinguish shared endpoint fans from mid-path overlap. Test swapped UUIDs and stable semantic aliases. | | Five-plus roots and disconnected component | Exposes current every-four-roots wrap and ordering discontinuity. No overlapping cards, reasonable route/area bounds and deterministic repeated result. | | OA/SA nested Allocation with ports, uniform subtree, folded ancestor and hidden route | Child interiors below reserved headers/footer, anchors on owner boundary, no fabricated ports, no semantic reassignment; hidden-route policy explicit; other view exact. | | Traceability mixed OA/SA with duplicate placement sentinels elsewhere | Flat visual graph stays flat, valid edge identities and other diagram exact. | | State machine with composite State, region, entry/exit boundary, internal/action rows and self-transition | State-specific legal boundary crossings, row reservations and no overwritten semantic state; shared Layout initialization remains valid even without a State Auto-layout button. | | Long ordinary/association labels, adjacent parallel labels, and #36 B/W/U State cases once integrated | Independently measure label/node, label/label and unrelated stroke/label intersections using actual displayed geometry; native ink/readability check separate from advance-box arithmetic. | | Dense/nonplanar or contradictory-anchor stress case | Finite time/work and explicit residual warnings/metrics; no claim all crossings can disappear. Preserve exact Undo and input on refusal. | ## Metrics and acceptance discipline Report a vector, not a single opaque beauty score: (1) hard invalid geometry/illegal obstacle penetration and sibling overlap area; (2) label-node/label-label intersection area and other-edge strokes entering labels; (3) distinct proper edge-pair crossings; (4) positive-length collinear shared segments; (5) bend count after duplicate/collinear simplification, path length/stretch and total view bounds. Define endpoint/ancestor exemptions before running: parent-child containment is intentional, a shared semantic endpoint is not a crossing, owner leads and legal State boundary crossings are not nonincident obstacle violations. Deduplicate segment-pair crossing events at the same physical point; count edge pairs separately. Count near-parallel clearance as a separate readability observation, not zero-width intersection. Use independent rectangle/segment math and full projected/routed outputs; export SVG and canvas must agree with those outputs. Include authored and derived labels, including association ends. A zero-crossing result obtained by coincident overlapping paths, gigantic detours, clipping labels or hiding edges fails the other metrics. Set fixture-specific route/area ceilings from a feasible manually declared witness rather than arbitrary post-hoc thresholds. Compare a second Arrange for idempotence, input-order permutation for determinism, pure camera changes for model invariance, then source Save/reopen and exact Undo/Redo. Unavoidable cases: nonplanar graphs cannot all be planar with distinct nonoverlapping edges; fixed attachment orders, nested boundaries, coincident semantic endpoints and required self-loops can constrain clearance. Existing authored labels can intentionally occlude nodes. Report constrained residuals honestly and avoid turning ordinary automatic layout into deletion of semantic edges or impossible universal no-crossing guarantees. The first useful milestone is a finite fixture/evaluator plus an explicit bend/label/hidden-route policy, then a prototype compared against current Layout. No need to expand into unrelated Process/Scenario geometry or redesign manual Arrange drag for this issue.