projection_runtime_lifecycle_spec - mark-ik/graphshell GitHub Wiki
Date: 2026-04-02 Status: Canonical runtime contract Priority: Graphlet-first implementation guidance; reusable by future domain projections
Related:
ASPECT_PROJECTION.mdprojection_interdomain_contract_spec.md../../technical_architecture/graphlet_model.md../../technical_architecture/unified_view_model.md../workbench/graphlet_projection_binding_spec.md../navigator/NAVIGATOR.md
This spec defines the runtime contract for projection descriptors, keys, invalidation, refresh, and lifecycle events in Graphshell.
It governs:
- projection descriptor identity,
- runtime cache keys,
- invalidation inputs,
- projection lifecycle events,
- linked and unlinked refresh behavior,
- graphlet as the first canonical runtime consumer.
It does not govern:
- graph truth mutation,
- workbench arrangement geometry,
- shell composition,
- or security policy for intelligence-facing source access.
The Projection aspect needs a runtime contract because projection objects are not one-frame UI conveniences.
They are:
- derived from explicit source authority,
- recomputable under stable rules,
- consumable by more than one domain,
- and expected to survive handoff between Navigator, Workbench, and Shell without semantic drift.
Normative rule:
- projection runtime state is keyed by derivation inputs and consumer scope,
- not by whichever widget happened to render first.
Every canonical projection must have a descriptor-level identity distinct from any one rendered instance.
Suggested shape:
pub struct ProjectionDescriptor {
pub projection_id: ProjectionId,
pub source_domain: ProjectionSourceDomain,
pub projection_kind: ProjectionKind,
pub derivation: ProjectionDerivation,
pub scope: ProjectionScope,
pub consumer_context: ProjectionConsumerContext,
pub policy: ProjectionPolicy,
}Required descriptor truths:
- what source authority the projection depends on,
- what kind of projection it is,
- what derivation rule defines membership,
- what scope or override path produced it,
- what consumer context is asking for it.
Descriptors are conceptual identity. Runtime instances need a cache key.
Suggested shape:
pub struct ProjectionRuntimeKey {
pub source_revision: u64,
pub descriptor_hash: u64,
pub consumer_context_hash: u64,
pub selector_hash: Option<u64>,
pub anchor_hash: Option<u64>,
pub scope_hash: u64,
}The exact field set may vary by projection class, but the runtime key must always distinguish:
- source revision or equivalent invalidation generation,
- projection descriptor meaning,
- consumer scope,
- derivation-specific inputs such as selectors, anchors, seed nodes, or filter context.
Normative rule:
- two runtime instances are the same cached projection only if their source revision and derivation inputs match.
The runtime should treat resolved projections as explicit instances rather than anonymous lists.
Suggested shape:
pub struct ProjectionRuntimeInstance {
pub runtime_key: ProjectionRuntimeKey,
pub descriptor: ProjectionDescriptor,
pub membership: ProjectionMembership,
pub frontier: Option<ProjectionFrontier>,
pub ranking: Option<ProjectionRanking>,
pub invalidation: ProjectionInvalidationState,
}For graphlet, this maps naturally to:
- member nodes and edges,
- anchors and optional primary anchor,
- frontier candidates,
- backbone or local ranking hints.
Future domains should reuse the same pattern rather than returning ad hoc row sets.
Projection invalidation must be explicit and observable.
Every projection class should classify invalidation sources under one or more of these families:
SourceTruthChangedScopeChangedSelectorChangedAnchorSetChangedConsumerContextChangedProjectionPolicyChangedPresentationHintChanged
-
SourceTruthChangedinvalidates membership correctness. -
ScopeChanged,SelectorChanged, andAnchorSetChangedinvalidate derivation correctness. -
ConsumerContextChangedmay preserve semantic membership while invalidating hosting suitability. -
PresentationHintChangedmust not be allowed to masquerade as membership truth change.
For graphlet, the minimum invalidation inputs are:
- edge-family selector change,
- seed-node change,
- active graph-view override change,
- source graph mutation affecting reachable membership,
- graphlet-local ranking or primary-anchor change when it affects frontier order.
Projection refresh should distinguish recomputation from UI re-render.
Recompute is required when membership or frontier may have changed.
Rebind is sufficient when the host or consumer context changed but the underlying membership remains valid.
If a linked host would change materially after recompute, the system must expose the change rather than silently rewriting the hosted structure.
This is already the right rule for graphlet-linked workbench groups and should generalize to future linked projections.
Projection runtime needs first-class lifecycle events so linked hosts, diagnostics, and future history/provenance systems can observe projection transitions.
Suggested event family:
pub enum ProjectionLifecycleEventKind {
Derived,
Refreshed,
Invalidated,
Linked,
Unlinked,
Forked,
Promoted,
Discarded,
}Suggested payload shape:
pub struct ProjectionLifecycleEvent {
pub event_id: ProjectionEventId,
pub projection_id: ProjectionId,
pub runtime_key: Option<ProjectionRuntimeKey>,
pub kind: ProjectionLifecycleEventKind,
pub source_domain: ProjectionSourceDomain,
pub consumer_context: ProjectionConsumerContext,
pub causality: ProjectionCausality,
}-
Derived: a projection instance was computed for use. -
Refreshed: an existing projection instance was recomputed under the same descriptor identity. -
Invalidated: an existing runtime instance is no longer trustworthy under its prior key. -
Linked: a host now explicitly tracks this projection. -
Unlinked: a host detaches from source-driven updates. -
Forked: a new descriptor or host artifact was created from an existing projection without remaining fully linked. -
Promoted: the projection crossed into a more durable named or saved form. -
Discarded: the runtime instance or ephemeral projection is intentionally dropped.
Linked hosts must keep two truths separate:
- the projection descriptor and resolved membership they are following,
- the host-local arrangement or presentation state they still own.
Normative rule:
- a linked host follows projection membership,
- but it does not surrender host-local geometry, focus state, tab order, or other arrangement-local carriers.
This remains the core graphlet-to-workbench rule and should generalize to future projection consumers.
Graphlet is the first reference implementation for this runtime contract.
That means Graphshell should use graphlet to validate:
- projection descriptors,
- runtime keys,
- invalidation families,
- linked versus unlinked refresh behavior,
- and lifecycle events.
If graphlet cannot be expressed cleanly under this model, the model is too weak. If graphlet can, the model is strong enough to generalize to other domains.
When a new domain needs cross-domain representation, it should reuse this runtime contract unless it has a clearly different source-revision and derivation model.
The default goal is one projection runtime vocabulary across domains, not a fresh cache or event model for each new projection class.
The projection runtime contract is doing its job when:
- derived representations can be keyed and refreshed without relying on UI widget lifetime,
- invalidation reasons are explicit and classifiable,
- linked and unlinked hosting use the same underlying lifecycle vocabulary,
- graphlet can act as the first clean reference implementation,
- future domains can reuse the same descriptor, key, invalidation, and lifecycle model.