10 KiB
Bounded placement compatibility design for #32
Recommendation: implement A's small shared core feasibility seam, then B's bounded separation pass as the first experimental consumer. This is one practical increment toward a native connection-aware planner, not a new router or a general constraint framework. Keep the released router, automatic-port projection policy and accepted Arrange reset/history contract fixed. C remains a diagnostic reference, not the next production step.
This source-only assessment follows reference2850b915 and the exact sparse diagnosis in placement-sparse-routing-diagnosis-20260925.md. No source changes, compiler, router or GUI execution were performed.
What the source requires
routing.rs:387–409maps actual projected semantic ports to exactly one tip and outward18-unit stub. Explicit non-port attachment preferences also constrain endpoints; otherwise four alternatives exist.route_diagram_impl:519–541handles promoted ownership, attachment suppression and existing authored route selection before endpoint generation. A planner must not approximate these choices independently.edge_obstacles:662–680preserves endpoint-ancestor headers, inflates obstacles by12;route_edge:830–840exempts only each lead's own endpoint owner from that lead's collision check.fast_path:1056–1062andgrid_path:1153–1159also reject stubs inside any obstacle. Terminal lead clearance is necessary, not sufficient for complete routing.projection/port_layout.rs:156–245chooses automatic sides from peer centers, semantic fallback and clear exits, then overrides manual anchors. Lines246+ grow capacity and enclose nodes after freezing side choices for that projection call. Reprojecting a new placement can therefore change automatic side, order and effective node size. Fixed semantic port identity does not mean fixed automatic coordinates.- The existing
examples/auto_layout_probe/layered.rs:27–83starts with Layout, rejects folded/omitted topology, resets routes, and uses one global largest label for all gaps. Its three passes use boundary-neighbor order but no final lead-compatibility feedback. It is a useful coordinate-assignment skeleton, not ready for adoption; sparse shared-run regressions remain independent of terminal clearance.
Alternatives and decision
| Alternative | Benefit | Concrete cost / failure | Decision |
|---|---|---|---|
| A. Shared core endpoint/lead feasibility, then constraint-aware placement | One authority for projection/attachments/promotions/ancestor obstacles. Planner can reserve actual required space and identify blockers. | A broad public routing API would ossify internals; pure advance spacing cannot predict every automatic-port flip. | Add only a small internal, read-only report reused by routing's lead guard and planner. Combine with actual reprojection. |
| B. Postplacement separation with actual reprojection | Small experiment on existing layered output; fixes the exact sparse gap without importing external routing. Tests whether locally enforcing clearance is useful before restructuring all coordinate assignment. | Arbitrary nudging can destroy layers/containment, oscillate as ports flip, and grow area indefinitely. A duplicate obstacle implementation would drift. | First consumer of A, with fixed sibling order, monotone gap constraints, finite rounds and atomic failure. Do not ship a free-form nudge loop. |
| C. ELK free automatic ports / coupled geometry | May let mature layout select compatible port-facing space; useful reference for quality ceiling. | Rust reselects automatic ports after import; importing ELK port locations would change application semantics or persist derived state. Node-level fixed/free policies complicate mixed authored/automatic anchors. Engine lead spacing differs from core. | Defer as implementation. Optional later diagnostic only if native bounded experiment fails a named case. |
Small implementation seam
Extract a shared predicate for endpoint-pair lead admissibility and stub containment using the already constructed endpoints/edge obstacles. Routing should call exactly that predicate without changing endpoint order, search costs or fallback. A planner-facing internal report can identify edge, end, actual owner, semantic port or attachment, tip/stub, blocker ID/bounds and which guard failed. It must preserve alternative endpoints: an unconstrained non-port end is not infeasible merely because one of its four sides is blocked. Report a hard incompatibility only when no allowed pair survives. For the first separation consumer, act only on singleton endpoints with a unique unrelated obstacle; leave ambiguous alternatives explicit rather than choosing a new persistent attachment.
Reuse the same effective-route selection (promotion suspends custom attachments; semantic ports take precedence) as routing. Avoid a public serializable constraints model, desktop dependency, whole routed path search or label measurement within this report. A private helper plus thin crate-visible wrapper is enough. For a diagnostic example outside the crate, expose only the narrow report if needed, or run the test inside core instead of making every routing internal public.
Finite native experiment
- Preserve the original Project and freeze Arrange reset eligibility. Compute the existing connection-aware sibling order/ranks once, including its explicit cycle fallback; keep released/reset control available for comparison. Do not use repeated Command::Layout inside repair.
- Project the candidate, obtaining actual automatic ports, projected sizes and obstacles. Obtain the shared incompatibility report. If none, proceed to final routing; this is not yet a quality pass.
- For an actionable blocker, add a separation inequality to the existing sibling/rank coordinate assignment, not an arbitrary node translation. Lift owner and blocker to distinct movable child subtrees under their lowest common visible ancestor; translate whole subtrees, including latent folded geometry. If no distinct movable groups exist, report unsupported constraint. Keep sibling order/orientation fixed. Choose a consistent existing ordering axis; for the exact vertically stacked sparse pair, require owner top >= blocker bottom +30. The30 is this 18-unit outward lead plus12 clearance, not a universal node gap. Prefer a small explicit numerical margin for new placement, leaving router epsilon unchanged.
- Recompute positions and parent enclosure from frozen minima and accumulated gap requirements. Reproject; side changes or capacity growth generate new constraints. Allow at most four repair rounds and a fixed global constraint/work bound. Never alternate swapping the same siblings or add gaps to already inflated stored sizes. Repeated identical geometry with unresolved constraints, opposite incompatible order constraints, excessive extent or exhausted rounds returns an explicit experimental failure and leaves original untouched. The first finite bound is an experiment limit, not proof of convergence.
- Route the final candidate once with the released router; reject newly introduced unrelated-node penetrations or NoClearPath. Separately assess existing quality vector and trace tasks against reset control. All metadata reconstruction happens once from original to final deltas; retain existing route entries/order/attachments/port anchors, clear only eligible pins/full labels, and preserve hidden/promoted metadata via accepted translation. Production adoption would apply one candidate through the existing transaction/history path with final validation.
This experiment should first use flat sparse and mixed fixtures plus one nested pair, not silently claim all OA/SA/State layouts. The current layered folded/omitted rejection must remain explicit until the separate full-forest policy is implemented. A repair pass does not resolve the prototype's enormous shared runs: if those persist, do not accept it merely because lead clearance succeeds.
Finite falsifiers and delivery point
- Exact sparse witness: at20-unit gap the target18-unit stub intersects the unrelated12-inflated obstacle; at30 plus declared placement margin the mandatory lead must clear. All tips/port identities follow final projection; no authored anchor edit or persisted bends.
- Automatic-side flip: moving a peer changes side; feasibility is reevaluated on the changed side. A stale first-projection pass is a failure. Shared semantic port must remain one port for all incident edges.
- Fixed-anchor witness and constrained non-port attachment: keep authored values exact; move geometry or fail. An unconstrained non-port edge with one clear side must not force unnecessary separation for its blocked alternatives.
- Nested obstacle: entire selected child subtree moves, children stay enclosed below headers, other branches and all semantic records remain exact. Folded/filtered cases must either satisfy the declared full-forest policy or return explicit unsupported; no unsupported-as-pass accounting.
- Same input twice must match; ten repeated full Arrange candidates must not accumulate clearance or oscillate. A two-cycle or container growth falsifies the proposed assignment scheme.
- Existing five-case frozen corpus: full conservation, actual projected geometry, warnings and unrelated penetration checks; retain label/crossing/shared-run/area values and original named trace tasks. Required outcome is no new hard routing failure plus at least a concrete connection-tracing improvement on a supported real scene. No scalar quality winner inferred from safety alone.
Stop after the shared seam, sparse/fixed-port tests and one bounded repair candidate are evaluated. If repair needs changing sibling order repeatedly or cannot fix the sparse witness without unacceptable displacement, move the constraints into coordinate assignment/ranking rather than adding more repair iterations. If it fixes terminal compatibility but old layered quality remains poor, next work is connection order/corridor allocation, not router penalty tuning. This yields a runnable, falsifiable increment without committing to an external engine or a general solver.