2026 02 24_layout_behaviors_plan - mark-ik/graphshell GitHub Wiki
Archived status: Closed / Archived 2026-04-02 — retained as implementation history; canonical active authority now lives in layout_behaviors_and_physics_spec.md and related active contracts.
Archived on: 2026-04-02
Reason: The behavior slices tracked here landed; this document is preserved as the historical execution record.
Status: Execution complete for the behavior slices originally tracked here. Phase 1.2 parent-aware spawn placement, Phase 2.1 degree-dependent repulsion, and Phase 2.2 domain clustering landed on 2026-04-02. Remaining work, if any, is follow-on tuning or optimization rather than missing feature slices from this plan.
Refactor note (2026-04-02): This file previously described Phase 3 as blocked on the layout injection hook. That status is stale. The hook and the frame-affinity slice landed under lane:layout-semantics, closed 2026-03-26.
Supersedes: 2026-02-19_layout_advanced_plan.md
Canonical spec: layout_behaviors_and_physics_spec.md — authoritative contracts for all behaviors described here. This file is now a historical execution plan plus current status ledger.
Latest checkpoint: ../PLANNING_REGISTER.md — see the 2026-03-26 lane:layout-semantics closure note.
Relates to: 2026-02-24_performance_tuning_plan.md (culling/perf ownership), graph_node_edge_interaction_spec.md (graph-surface camera and viewport expectations), semantic_tagging_and_knowledge_spec.md (UDC parsing, semantic clustering semantics, canonicalization), 2026-02-24_physics_engine_extensibility_plan.md (physics engine architecture, ExtraForce extension model, thematic preset designs), multi_view_pane_spec.md (graph-pane isolation and per-view layout ownership), ../workbench/graph_first_frame_semantics_spec.md (Frame identity and membership authority).
This plan covers behavioral layout features, not frame-time optimization.
Reviews of RustGrapher and WasmGrapher reinforce two boundaries already implicit in this plan.
- Behavioral layout policy and force composition should remain Graphshell-owned rather than widget-owned. External graph libraries are useful as evidence about engine structure, not as authority over Graphshell semantics.
- Acceleration structures such as Barnes-Hut or quadtree indexing are legitimate follow-on work, but only after simulation ownership and per-view runtime boundaries are clean. They are not a reason to bypass the injection-hook and policy-first sequencing.
Practical consequence: keep prioritizing explicit behavior hooks, per-view isolation, and deterministic policy gates before deeper performance specialization.
- Keep force-behavior toggles policy-driven (
CanvasRegistry/ resolved surface profile), not hardcoded in render callsites. - Preserve existing intent and lifecycle boundaries (
apply_intents-> reconcile -> render). - Treat this plan as additive to the current physics system; avoid introducing a parallel layout engine.
- Scope applies to graph panes only (
CanvasRegistrysurface behavior). Node viewer panes and tool panes are out of scope.
Multi-view note: Graph-view layout state is isolated per GraphViewId as defined in multi_view_pane_spec.md. Unless explicitly stated otherwise, behaviors in this plan must not implicitly overwrite a sibling view's local simulation state.
- Structural graph mutations reheat physics without zeroing velocity state.
- Profile-driven gravity strength remains wired through the resolved physics profile into the runtime physics state.
- The post-physics extension path is present and used for semantic/frame-affinity behavior.
- Frame-affinity regions are derived at runtime and rendered as soft organizational backdrops behind nodes.
- Frame-affinity force is gated by
CanvasRegistry.zones_enabled. - Graph-backed frame semantics are aligned on
ArrangementRelation/frame-memberauthority rather than a separate zone store.
- Force-strength tuning and scenario-based regression coverage can continue as separate polish work.
- Deeper acceleration work such as Barnes-Hut or quadtree indexing remains out of scope for this plan.
Status: Landed.
Outcome:
- Adding nodes/edges through graph mutation paths reheats physics by setting
physics.is_running = true. - The behavior resumes simulation rather than resetting the velocity state.
- Snapshot/load replay is not treated as a user-visible reheat path.
Status: Landed.
Original goal:
- If node creation has a semantic source/parent, place at
parent_position + jitter. - Ensure the create-new path carries parent identity so placement does not need a later graph search.
Outcome:
- Shared anchored-placement logic now backs both host child-webview creation and generic anchored new-node placement.
- When a known semantic source/parent is available, new nodes are placed near that anchor rather than center-spawned.
- Center/centroid fallback remains only for cases without an anchor.
Status: Landed.
Outcome:
- The resolved physics profile remains the authority for gravity strength.
- Gravity strength is mapped into runtime physics tuning and applied to the center-gravity extra in the active physics state.
Status: Foundation landed; planned force slices remain partially open.
The former prerequisite is now resolved in practice through the existing post-layout/post-physics extension path:
- Render flow pulls the latest layout state, updates runtime physics state, then applies extension forces.
- Resolved per-view physics profile data builds a
GraphPhysicsExtensionConfig. -
CanvasRegistry.zones_enabledgates frame-affinity behavior at the call site.
This means the original Phase 3 blocker is gone.
Status: Landed.
Original goal: De-clutter high-degree hub topologies.
Outcome:
-
PhysicsProfile.degree_repulsionnow gates a real post-physics force pass. - Nearby nodes receive additional separation bias based on local degree, improving high-degree hub decluttering without introducing a parallel layout engine.
Status: Landed.
Original goal: Pull same-domain nodes into soft spatial neighborhoods.
Outcome:
-
PhysicsProfile.domain_clusteringnow gates a real post-physics force pass. - Nodes are grouped by registrable-domain heuristic and softly attracted toward the domain centroid.
- The force remains independent from UDC semantic clustering.
Note: This remains independent from UDC semantic clustering; both may be enabled together.
Status: Core behavior landed. lane:layout-semantics execution slices closed 2026-03-26.
Terminology: The historical name "Magnetic Zones" is a legacy alias only. Canonical framing is frame-affinity organizational behavior under graph-first frame semantics. Do not use
Zone,MagneticZone, ornode.zone_idin new code or docs. The authoritative behavioral contract remainslayout_behaviors_and_physics_spec.md §4.
- The post-physics extension hook is present in the render/physics path and is used to apply frame-affinity force.
- Graph-first frame semantics are settled enough for implementation use, with newer alignment notes treating durable
ArrangementRelation/frame-memberedges as the long-term authority.
The original Phase 3 data model has been refined by later frame-semantics work. The current framing is:
FrameAffinityRegion {
frame_anchor: NodeKey,
members: Vec<NodeKey>,
centroid: Vec2,
strength: f32,
}
ArrangementRelation(FrameMember) edges // durable frame-membership authority
GraphFrame.member_nodes: Vec<NodeKey> // transitional/derived projection
Per-node membership views // derived/read-model projections where exposed
A node may belong to zero, one, or many frames. There is no node.zone_id field and no separate persisted zone store.
Frame-affinity regions are derived runtime state, not an independent persisted workspace entity.
- Durable frame identity and membership live in graph-backed frame semantics.
- Any region geometry cache is derivable runtime state and must not create a second durable identity.
- Snapshot roundtrip persists graph-backed frame membership truth; affinity projection recomputes from that source.
- Frame-affinity force is applied after the primary physics step through the extension path.
- The force is a soft bias toward frame centroids, not a hard positional constraint.
- Behavior is gated by
CanvasRegistry.zones_enabled. - When a node has multiple frame memberships, the resulting force contributions compose deterministically.
- A semi-transparent backdrop with label is rendered below member nodes for active frame-affinity regions.
The core execution sequence is now complete for the force/render slice:
-
FrameAffinityRegionexists as a derived runtime type. - Frame-affinity force is computed from graph-backed frame membership and applied through the extension path.
- Frame-affinity backdrop rendering is present and visually gated by
zones_enabled.
The broader frame interaction surface now lives primarily under the workbench/frame semantics lane rather than this file's original checklist. graph_first_frame_semantics_spec.md, workbench_frame_tile_interaction_spec.md, and 2026-03-26_frame_layout_hint_spec.md should be treated as the current sources for frame lifecycle, tile-group materialization, and related UI interactions.
See layout_behaviors_and_physics_spec.md §8 for the full acceptance criteria table. Current summary:
- Structural mutation reheats physics.
- Parent-aware spawn near semantic parent is landed.
- Gravity strength remains profile-driven.
- Degree repulsion is configured and applied as a force slice.
- Domain clustering is configured and applied as a force slice.
- Frame-affinity regions are derived runtime state; no separate zone store exists.
- Multi-membership frame-affinity forces compose deterministically.
- Frame-affinity applies as a soft post-physics bias, not a hard override.
- Frame membership authority is graph-backed; affinity projection recomputes from persisted graph state.
- Tune force constants and expand scenario-level regression coverage if behavior polish is needed.
- Keep any deeper optimization work, such as Barnes-Hut or quadtree acceleration, separate from the semantic/layout ownership already established here.