graphlet_projection_binding_spec - mark-ik/graphshell GitHub Wiki
Date: 2026-03-25 Status: Canonical / Active Scope: Defines how Workbench tile groups bind to canonically defined graphlets, how graphlet projection scopes compose for arrangement workflows, and when the user must be warned before a selector change rewrites linked graphlet structure.
Related:
-
../../technical_architecture/graphlet_model.md— canonical graphlet semantics across domains ../canvas/multi_view_pane_spec.mdworkbench_frame_tile_interaction_spec.mdworkbench_layout_policy_spec.md../navigator/NAVIGATOR.md../navigator/navigator_interaction_contract.md-
../graph/2026-03-14_graph_relation_families.md— family-oriented Navigator modes and projection semantics ../../TERMINOLOGY.md-
../../../archive_docs/checkpoint_2026-03-21/2026-03-20_arrangement_graph_projection_plan.md— historical background
Alignment note (2026-03-27): newer Navigator planning distinguishes between graphlet-oriented projection forms and relation-family-oriented section/mode forms. This spec is only about the graphlet-binding side of that model:
- graphlets remain the canonical object for ego/corridor/component/frontier and other bounded local-world derivations,
- relation-family modes (
Workbench,Containment,Semantic,All nodes) remain Navigator-owned projection shapes defined ingraph/2026-03-14_graph_relation_families.md, - Workbench binding must be able to consume graphlets without redefining them, and must not accidentally treat all Navigator family-oriented modes as graphlet links.
Graphshell already distinguishes graph truth from workbench presentation, but Workbench still needs a precise answer for how arrangements relate to graphlets:
- graphlet membership depends on which selectors and derivation rules are active,
- Workbench must consume graphlets without redefining them,
- a tile group may either stay linked to a graphlet definition, remain as an unsaved session group, or fork into a new pinned graphlet.
This spec makes the binding model explicit so graph filtering, Navigator projection, and Workbench grouping all speak the same language.
Practical boundary:
- if the user is in a graphlet-oriented workflow, Workbench may bind to that graphlet and expose linked/unlinked behavior;
- if the user is in a family-oriented Navigator mode, Workbench may still open
nodes or arrangements from that projection, but that does not automatically
imply a
GraphletBinding::Linkedrelationship.
The canonical graphlet definition lives in ../../technical_architecture/graphlet_model.md.
For Workbench purposes, the important constraints are:
- graphlets are projection-derived unless explicitly promoted elsewhere,
- graphlets are scope-sensitive and recomputable,
- graphlets are not synonymous with tile groups or frames,
- Workbench may bind to a graphlet, but does not become the owner of graphlet truth.
When this spec discusses graphlets, it is discussing graphlets as consumed by Workbench binding and routing.
Important non-equivalence:
- a relation-family section or Navigator mode is not automatically a graphlet,
- a graphlet may be derived using selectors that mention relation families, but the resulting object is still a bounded graphlet with anchors and derivation rules, not merely "whatever rows are visible in Navigator."
The active graphlet definition is carried by an EdgeProjectionSpec.
Suggested shape:
pub struct EdgeProjectionSpec {
pub selectors: Vec<RelationSelector>,
pub source: ProjectionSource,
}
pub enum ProjectionSource {
GraphDefault { graph_id: GraphId },
GraphViewOverride { graph_view_id: GraphViewId },
SelectionOverride {
graph_view_id: GraphViewId,
seed_nodes: Vec<NodeKey>,
},
}The selectors field names which edge families/sub-kinds contribute to
connectivity. The source field names where that choice came from.
This is a graphlet-projection contract, not a generic contract for every
Navigator projection mode. Family-oriented Navigator modes may reuse similar
selector vocabulary, but EdgeProjectionSpec here exists specifically to
define bounded graphlet derivation for binding/routing.
Projection resolution follows:
SelectionOverride -> GraphViewOverride -> GraphDefault
Meaning:
- a selection-level override affects only the targeted selection workflow
- a graph-view override affects only that
GraphViewId - a graph-level default applies when no narrower override exists
- Sets the default edge projection for a
GraphId. - Affects graph views that have not declared their own override.
- Does not implicitly overwrite a graph-view or selection override.
- Applies only to the target
GraphViewId. - May produce graphlets different from sibling graph views over the same graph.
- Must not mutate other graph views' graphlet projections.
- Applies only to the selected nodes and the workflow launched from them.
- This is the contract behind "multi-select some nodes, turn on History or Traversal edges, then enter the workbench with the resulting graphlet warm."
- Must not rewrite unrelated graphlets elsewhere in the graph view.
When the user has a multiselection and is choosing which relation families/selectors to project, the selector tool must rank candidates by how well they explain or connect the current selection.
Minimum ranking rule:
- rank selectors that produce a component containing all selected nodes above selectors that only cover a subset
- among partial matches, rank selectors by selection coverage count: number of selected nodes that participate in at least one edge matching that selector
- use largest resulting component size containing any selected node as the next tie-breaker
- use domain-specific preference only after coverage-based ranking
This keeps the tool honest: when a user selects five nodes, a selector that meaningfully touches all five should appear above one that only explains two.
Suggested score shape:
pub struct SelectionProjectionCandidate {
pub selector: RelationSelector,
pub selected_node_coverage: usize,
pub total_selected_nodes: usize,
pub largest_component_size: usize,
pub fully_connects_selection: bool,
}Some multiselections will have no single relation selector that fully connects all selected nodes.
Typical examples:
- a hand-picked bundle of recent nodes that share no durable semantic edge
- a loose investigation set where only some members have traversal history
- new nodes that are related primarily by creation order or session context
In those cases, the selector tool should still surface useful options instead of showing an empty or misleading list.
Required behavior:
- rank partial selectors using the coverage rule in §3.4
- if no selector fully connects the selection, offer chronological organization as a fallback mode
- chronological fallback may use:
- navigation/history ordering, when available
- node spawn/creation ordering, when history does not connect the full set
Important constraint:
- chronological fallback is an organization mode, not proof that all nodes belong to one edge-defined graphlet under the normal selector model
This should be legible in UI copy. For example:
- "No single relation family connects all 6 selected nodes"
- "Best coverage: History (4/6), Traversal (3/6), User Grouped (2/6)"
- "Open as chronological sequence instead"
Graphlet projection and workbench arrangement are separate concerns.
- The graphlet answers: "which nodes and edges belong to the currently resolved meaningful graph subset under the active derivation rules?"
- The tile group answers: "which panes are currently arranged together in the workbench, and how?"
The relationship between them must therefore be explicit.
Suggested shape:
pub enum GraphletBinding {
UnlinkedSessionGroup,
Linked {
projection: EdgeProjectionSpec,
seed_nodes: Vec<NodeKey>,
member_nodes: Vec<NodeKey>,
},
}Meaning:
-
UnlinkedSessionGroup: the tile group is just a session arrangement. It keeps its current tiles and layout regardless of future graphlet changes, but it is not yet durable graph truth. -
Linked: the tile group is explicitly attached to a graphlet definition. Its roster is expected to correspond to the graphlet produced by the stored projection and seeds.
Important persistence rule:
-
UnlinkedSessionGroupis the honest "wait" state for a workbench the user wants to keep open without immediately rewriting graphlet truth. - durable persistence should route through an explicit graphlet save/fork path, not by pretending an unlinked arrangement is already a first-class graphlet.
Non-goal clarification:
- opening content from a Navigator family section does not, by itself, create a
Linkedgraphlet binding; -
Linkedis reserved for arrangements that explicitly follow a bounded graphlet definition and therefore must participate in selector/binding warning logic.
When workbench changes are propagated back into a linked graphlet, the system must preserve two distinct kinds of information:
- the member roster delta: which nodes were added, removed, or rebased
- the attachment causality for each added member: why this node belongs, from where it was spawned, and which relation/event carriers should be asserted
The roster delta defines the resulting graphlet shape. The causality carrier defines the meaning of each membership change.
Required rule:
- Workbench -> graphlet propagation must not be modeled as "replace member set" alone.
- For each newly attached member, the reconciliation contract must carry enough
information to decide whether to emit:
- a traversal event,
- a semantic relation assertion,
- an arrangement assertion,
- provenance metadata,
- or a combination of those.
Examples:
- a node opened by following a member node should usually carry a source member, a navigation trigger, and any relevant selector context before the graphlet is rewritten
- a pre-existing node manually folded into the current graphlet may justify a semantic or arrangement assertion without inventing a traversal record
- a freshly spawned node should preserve the source member and user action that caused the spawn so later graph placement and explanation are not arbitrary
This keeps graphlet membership honest: the roster says what changed and the causality carrier says why it changed.
If a tile group is Linked, the workbench must treat changes to the underlying
graphlet as structurally significant. If recomputation would change the member
set, the system must not silently proceed as though nothing happened.
Layout geometry is not graphlet truth.
Even when a tile group is linked to a graphlet:
- tab order
- split geometry
- focused tile
- docked/floating arrangement choices
remain workbench arrangement state, not graph truth.
The UI must make linked vs unlinked state legible.
Minimum model:
- Linked to graphlet — selector changes may change membership; the group is structurally coupled to graphlet recomputation
- Unlinked session group — selector changes do not rewrite the group's roster, and the current arrangement is not yet durable graphlet truth
This distinction may be shown via badge, chip, context-menu label, or pane chrome subtitle, but it must be visible somewhere the user can inspect before changing selectors.
Changing active edge selectors can:
- overwrite a previous projection choice
- reshape a linked graphlet
- split one linked graphlet into several
- merge previously separate linked graphlets
- cause a tile group to cease matching the graphlet it was following
Those cases require explicit warning.
Warn before applying a selector change when the target scope already has a non-empty explicit projection that would be replaced.
Examples:
- replacing a graph-view override with a different selector set
- applying a new selection override to a selection that already has one
- promoting a selection override into a linked workbench group that already follows another projection
Warn before applying a selector change when a Linked tile group's recomputed
member set would differ from its current linked member set.
Difference includes:
- one or more members would be added
- one or more members would be removed
- the graphlet would split into multiple components
- the graphlet would merge with another linked graphlet
Unlinked session groups do not require a graphlet-structure warning when selectors change, because their roster is no longer governed by graphlet projection.
They may still show an informational notice such as "current arrangement is not linked to graphlet structure," but this is not a blocking confirmation.
If any node in a Linked binding's seed_nodes is deleted from graph truth
(tombstoned), the binding can no longer resolve its projection. The system
must not silently leave the binding in a Linked state with an unresolvable
projection.
Required behaviour on seed node deletion:
- Detect that a
Linkedbinding's seed set now contains a tombstoned key. - Emit a diagnostics event (severity
Warn) identifying the affected binding and the deleted seed keys. - Offer the user a two-outcome choice (no Cancel, since deletion has already
occurred):
-
Rebase to remaining seeds — if any non-tombstoned seeds remain,
recompute the graphlet from the surviving seed set and update
member_nodes. Binding staysLinked. -
Keep as unlinked session group — convert the binding to
UnlinkedSessionGroup, preserving the current tile group roster as-is.
-
Rebase to remaining seeds — if any non-tombstoned seeds remain,
recompute the graphlet from the surviving seed set and update
- If all seed nodes are tombstoned, the binding cannot stay
Linked. Auto-convert toUnlinkedSessionGroupand emit aWarnevent.
This case is distinct from a selector change: the selector is unchanged but the projection source is partially or fully invalid.
Two different situations require explicit user choice:
- a selector change would break or overwrite a linked graphlet relationship
- a linked workbench returns to graph context with a member roster that no longer matches the linked graphlet
These situations may share one confirmation surface, but they must preserve the same semantic outcomes.
When a linked workbench must be reconciled, the confirmation surface must offer the following outcomes:
-
Apply and keep linked
- Commit the pending selector or roster change.
- Recompute or rewrite the linked graphlet.
- Reconcile the tile group's member roster to the resulting graphlet.
- Emit the required relation assertions, traversal events, and provenance carriers for every added or removed member.
-
Keep as unlinked session group
- Preserve the current tile group roster/layout as-is.
- Commit no graphlet rewrite.
- Convert the tile group to
UnlinkedSessionGroup. - Allow the user to continue working and choose a later explicit save/fork operation.
-
Save as new graphlet fork
- Create a new pinned graphlet derived from the currently linked graphlet.
- Copy the linked graphlet's derivation context, then apply the current workbench member delta to the fork.
- Preserve provenance to the parent graphlet and the reconciliation event that produced the fork.
-
Cancel / discard pending change
- Do not change selectors or graphlet membership.
- Preserve the previously linked graphlet as the source of truth.
- If the current workbench roster was the pending change, restore the last synchronized linked roster.
This choice is the explicit answer to whether arrangement should remain associated with graphlet structure or not. It must not be an implicit side effect hidden behind selector toggles.
"Save as new graphlet fork" is not an instruction to apply the current workbench to some unrelated graphlet.
Required meaning:
- the current linked graphlet is the parent
- the new graphlet inherits the parent graphlet's derivation context and seed lineage
- the current workbench roster is interpreted as a delta over that parent
- the fork records explicit provenance back to the parent graphlet
This makes the fork a graphlet-native operation rather than an arrangement-only copy.
- User selects nodes in a graph view.
- User enables additional edge families/selectors for that selection.
- System creates a
SelectionOverride. - Graphlet is computed from the selected nodes under that projection.
- Workbench opens with the warm members of that resulting graphlet.
- The resulting tile group starts as
Linkedunless the user explicitly asked for an unlinked session group.
This workflow must not alter unrelated graphlets elsewhere in the graph view.
- User selects several nodes.
- System ranks candidate selectors by coverage across the current selection.
- No selector yields a graphlet containing all selected nodes.
- User may still choose:
- the highest-coverage selector, producing a partial graphlet rooted in the covered subset, or
- chronological fallback, opening the selection as an ordered workbench sequence
- The resulting workbench surface must indicate whether it is:
- linked to a true edge-projected graphlet, or
- an ordered unlinked session group derived from chronology/session order
The system must not silently pretend that chronology created a normal connected graphlet when it did not.
- User changes graph-wide selectors.
- System evaluates all inheriting graph views and linked groups that depend on the graph default.
- If no linked groups would change membership, apply immediately.
- If any linked group would change membership, show the confirmation choices in §7.
- User changes selectors for one
GraphViewId. - Only that view's graphlets and linked groups are evaluated.
- Sibling views over the same
GraphIdremain unchanged.
- User chooses an unlinked session group.
- User explicitly selects "Link to current graphlet" or equivalent.
- System stores
GraphletBinding::Linkedusing the current projection and seed context. - Future selector changes once again participate in the warning flow.
- User opens a linked graphlet into the workbench.
- User changes the effective member roster by adding, removing, or rebasing graphlet members.
- For each added member, the workbench records attachment causality: source member or anchor, triggering action, and the candidate relation/event carriers needed to justify the addition.
- User returns to graph context.
- If the current roster and the last synchronized linked graphlet differ, the system shows the reconciliation choices from §7.1.
- If the user chooses Apply and keep linked, the graphlet rewrite uses both the member roster delta and the recorded attachment causality.
- If the user chooses Keep as unlinked session group, the workbench stays open as a session arrangement without rewriting graphlet truth.
- If the user chooses Save as new graphlet fork, the parent graphlet is copied and the current roster delta is applied to the fork.
Navigator graphlet rows must read from the same resolved projection model:
- graph default when no narrower scope exists
- graph-view override when present
- selection override when the Navigator is projecting that workflow context
Navigator rows must not assume that only durable UserGrouped or
FrameMember edges define graphlet membership.
Compatibility fallback is allowed during migration, but the intended authority is selector-resolved graphlet projection, not the old durable-only interpretation.
Companion rule:
- family-oriented Navigator sections/modes defined in
graph/2026-03-14_graph_relation_families.mdremain valid Navigator projections even when no linked graphlet binding exists; - the Workbench should only invoke this spec's binding warnings and linked/ unlinked semantics when the active workflow is actually graphlet-linked.
Workbench routing must distinguish between:
- opening a node into an existing linked graphlet group
- opening a node into an unlinked session group
- creating a new linked group from a selection override
- opening nodes from family-oriented Navigator projection without establishing a graphlet binding
The current durable-only routing helpers are compatibility defaults, not the final semantic model.
pub struct ResolvedGraphletContext {
pub projection: EdgeProjectionSpec,
pub seed_nodes: Vec<NodeKey>,
pub members: Vec<NodeKey>,
}
pub struct GraphletMemberDelta {
pub added: Vec<NodeKey>,
pub removed: Vec<NodeKey>,
pub rebased_seeds: Vec<NodeKey>,
}
pub struct MemberAttachmentContext {
pub member: NodeKey,
pub source_member: Option<NodeKey>,
pub trigger: Option<NavigationTrigger>,
pub selectors: Vec<RelationSelector>,
pub action_label: Option<String>,
pub member_was_newly_spawned: bool,
}
pub struct GraphletForkOrigin {
pub parent_graphlet_id: String,
pub parent_seed_nodes: Vec<NodeKey>,
pub fork_reason: String,
}
pub enum SelectorChangeImpact {
NoStructuralChange,
OverwritesExistingProjection,
ChangesLinkedMembership {
added: Vec<NodeKey>,
removed: Vec<NodeKey>,
},
}
pub enum GraphletRewriteChoice {
ApplyKeepLinked,
KeepAsUnlinkedSessionGroup,
SaveAsNewGraphletFork,
Cancel,
}
pub struct GraphletReconciliationProposal {
pub linked_context: ResolvedGraphletContext,
pub current_member_delta: GraphletMemberDelta,
pub attachment_contexts: Vec<MemberAttachmentContext>,
pub fork_origin: Option<GraphletForkOrigin>,
}
pub struct SelectionProjectionCandidate {
pub selector: RelationSelector,
pub selected_node_coverage: usize,
pub total_selected_nodes: usize,
pub largest_component_size: usize,
pub fully_connects_selection: bool,
}These are not mandated byte-for-byte, but the reducer/UI contract must carry equivalent information.
This model is correctly implemented when:
- Graphlet computation is explicitly driven by resolved selectors, not a hidden durable allowlist.
- Selector changes can be applied at graph, graph-view, and selection scope.
- A selection-scoped graphlet workflow can warm/open only the resulting graphlet without mutating unrelated graphlets.
- Tile groups can be inspected as either linked or unlinked session groups.
- Selector changes or workbench-return reconciliation that would rewrite a linked graphlet produce the explicit outcomes from §7.1.
- Unlinked session groups survive selector changes without structural mutation.
- Navigator and workbench routing consume the same resolved graphlet context.
- Multiselection selector suggestions are ranked by selection coverage before any cosmetic or domain-specific preference ordering.
- When no selector fully connects the selection, the system can offer chronological organization without mislabeling it as a normal connected graphlet.
- Applying a dirty linked workbench back to graph truth uses both member roster deltas and per-member attachment causality rather than replacing the graphlet roster blindly.
- Saving a changed linked workbench without rewriting the parent graphlet can create a new pinned graphlet fork with explicit parent provenance.