graph_first_frame_semantics_spec - mark-ik/graphshell GitHub Wiki
Date: 2026-03-01
Status: Canonical interaction contract
Priority: Immediate terminology + authority alignment
Related:
workbench_frame_tile_interaction_spec.md../canvas/graph_node_edge_interaction_spec.md../canvas/multi_view_pane_spec.md../aspect_command/command_surface_interaction_spec.md../aspect_input/input_interaction_spec.md../subsystem_ux_semantics/ux_tree_and_probe_spec.md../2026-03-01_ux_migration_design_spec.md-
../canvas/2026-03-14_graph_relation_families.mdโ ArrangementRelation as the canonical graph-edge backing for frame membership (ยง2.4) -
WORKBENCH.mdโ current Workbench/Graph boundary and graph-backed frame membership note ../../../TERMINOLOGY.md
Alignment note (2026-03-27): newer architecture notes treat frame membership
as graph-backed arrangement truth, with Workbench remaining the owner of handle
lifecycle, routing, split geometry, and session-only arrangement until
promotion. This spec therefore treats GraphFrame.member_nodes as a
transitional/derived carrier rather than the long-term semantic authority.
This spec defines the canonical cross-tree semantics for Frame as a graph-first organizational object.
It establishes:
- Frame identity and lifecycle in graph scope.
- Frame address semantics under the internal address-as-identity model (
verso://runtime canonical namespace, legacygraphshell://compatibility alias). - Workbench handles as open views over graph frames.
- Membership synchronization between graph truth and workbench interactions.
- Close vs delete semantics (non-destructive close by default).
- UxTree exposure requirements for frame-aware automation and accessibility.
This is a semantic authority spec. It does not define rendering geometry.
A Frame is a graph organizational entity that may exist independently of any
open workbench handle.
- Frame identity lives in graph scope.
- Workbench can open, focus, close, split, and dock frame handles.
- Closing a handle does not remove frame identity.
A workbench frame UI element is an open handle over a graph frame:
-
OpenFrameHandle(frame_id)creates/activates a handle. -
CloseFrameHandle(frame_id)closes the handle only. -
DeleteFrame(frame_id)removes graph frame identity (destructive).
- Closing a node pane is like closing an open file view.
- Closing a frame handle is like closing an open folder view.
- Neither operation deletes the underlying graph object.
Frames intentionally cross the semantic boundary between graph and workbench:
- Graph scope is the authority for frame identity and membership.
- Workbench scope is the authority for tilegroup layout, docking, focus, and handle lifecycle.
- A tilegroup is a workbench handle/projection over a
Frame, not a second frame identity.
Guardrail wording:
- Preferred: "tilegroup is a handle over a frame."
- Avoid: wording that implies strict identity equivalence between tilegroup lifecycle and frame lifecycle.
- Consequence: close/split/dock remain non-destructive UI operations, while
DeleteFrameremains an explicit destructive graph operation.
struct GraphFrame {
frame_id: FrameId,
label: String,
color_token: FrameColorToken,
member_nodes: Vec<NodeKey>,
created_at: Timestamp,
updated_at: Timestamp,
}
struct NodeFrameMembership {
node: NodeKey,
frames: Vec<FrameId>,
}Every frame has a canonical internal address:
verso://frame/<FrameId>
Legacy note:
- Older docs may still refer to
graphshell://frame/<FrameId>as the original spec basis. - Runtime canonical formatting should emit
verso://frame/<FrameId>.
Address rules:
- The address is issued when the frame identity is created.
- The address is stable for the lifetime of that frame.
- The address resolves to the frame's graph node under the address-as-identity rule in
TERMINOLOGY.md. - Closing a workbench handle does not invalidate the address, because handle closure is not graph deletion.
-
DeleteFrame(frame_id)removes the frame identity and therefore removes the live resolution ofverso://frame/<FrameId>.
The frame address is the canonical identity bridge between:
- graph storage (
GraphFrame), - workbench handles (
OpenFrameHandle/CloseFrameHandle), - frame-membership visualization on the canvas.
The canonical long-term authority for durable frame membership is
ArrangementRelation / frame-member edges in the graph
(EdgeKind::ArrangementRelation { sub_kind: "frame-member" }).
member_nodes: Vec<NodeKey> remains a useful implementation-facing or
serialization-facing projection while migration and compatibility work continue,
but it should be read as a transitional/derived view rather than the desired
semantic source of truth.
Under the graph-backed model:
- Frame membership is a set of durable
frame-memberedges between the frame node and its member tile nodes. - Named frames produce durable
frame-memberedges; unnamed/session frames produce session-only edges. - The
GraphFrame.member_nodesfield becomes a derived view over these edges, not the authoritative store. -
AddNodeToFrame/RemoveNodeFromFrameintents assert / retract the correspondingArrangementRelationedge.
Implementation note: if any code path still treats GraphFrame.member_nodes as
authoritative, that should be understood as transitional debt rather than the
target architecture. See canvas/2026-03-14_graph_relation_families.md ยง2.4
and WORKBENCH.md.
- Nodes may be members of zero, one, or many frames.
- Membership is explicit and persisted in graph scope.
- Workbench views may be filtered to one active frame, but that is a view choice, not a membership constraint.
Frame is the semantic authority. MagneticZone is legacy visualization wording and, where retained, refers only to the canvas presentation of a frame-affinity region.
Canonical wording:
- Use
Framefor the graph object. - Use
Frame membershipfor node-to-frame organizational links. - Use
Frame-affinity regionfor the visual/layout projection of a frame on the graph canvas. - If
MagneticZoneappears in older docs, read it as a legacy alias for the visual frame-affinity region, not a separate semantic object.
Workbench tile operations that imply organization must update graph memberships:
| Workbench operation | Graph effect |
|---|---|
| Add node tile to frame | AddNodeToFrame(frame_id, node_key) |
| Remove node tile from frame | RemoveNodeFromFrame(frame_id, node_key) |
| Create frame from selection | CreateFrameFromSelection(node_keys...) |
| Merge frames |
MergeFrames(source, target) updates membership truth and any derived membership views |
Authority note:
- Graph owns whether a durable frame-membership relation exists.
- Workbench owns how the corresponding handle, tilegroup, split, or tab context is realized on screen.
- Session-only arrangement may exist before promotion, but promotion must be the explicit bridge into durable frame-membership truth.
| Action | Destructive | Required behavior |
|---|---|---|
| Close frame handle | No | Remove only workbench handle; preserve frame and memberships |
| Close node pane | No | Remove only pane handle; preserve node and memberships |
| Delete frame | Yes | Remove frame identity and membership links; requires explicit confirmation |
| Delete node | Yes | Remove node identity per node lifecycle policy |
-
Ctrl+Won frame context maps toCloseFrameHandle(non-destructive). -
DeleteFrameis available only via explicit destructive command path (Command Palette+ confirmation).
Frames may project a visual region on graph canvas for orientation:
- each frame has a stable color token,
- member nodes may be softly biased toward a frame-affinity centroid,
- affinity is a visual/layout hint only,
- no implied identity duplication.
The frame-affinity region is a visual projection of the same frame identified by verso://frame/<FrameId>. It is not a second frame-like object and must not drift into a separate storage identity.
If a node belongs to multiple frames:
- frame badges/chips show all memberships,
- active-frame context gets primary highlight,
- secondary memberships remain visible in compact form.
UxTree must expose frame semantics for automation/accessibility:
- frame handles in workbench chrome,
- node frame-membership states in node semantic subtree,
- invokable actions:
OpenFrameHandleCloseFrameHandleAddNodeToFrameRemoveNodeFromFrame-
DeleteFrame(destructive)
-
uxnode://workbench/sidebar/frame[{frame_id}](legacy path:uxnode://workbench/workbar/frame[{frame_id}]) uxnode://workbench/tile[graph:{graph_view_id}]/graph-canvas/node[{node_key}]/frame-memberships
- Closing a frame handle does not remove graph frame identity.
-
verso://frame/<FrameId>remains stable while the frame exists and stops resolving only afterDeleteFrame(with legacygraphshell://frame/<FrameId>accepted only as a compatibility alias while migration remains active). - Frame membership mutations in workbench are reflected in graph state.
- Node can belong to multiple frames and memberships persist across restart.
- Deleting frame is explicit and separate from close.
- UxTree surfaces frame handles and frame-membership actions.
- Canonical docs in this lane use frame-first terminology.
- Tilegroup operations (
close,split,dock) do not create or delete frame identity unless an explicit frame-destructive command is invoked.
- This is a terminology + semantic authority update, not a
MagneticZoneruntime migration. - Existing canvas docs that mention
MagneticZoneshould be updated to frame-affinity terminology where they describe organizational behavior. - If a future dedicated non-frame clustering feature is introduced, it must use
a distinct canonical term and explicit scope, not
MagneticZonereuse.