frame_persistence_format_spec - mark-ik/graphshell GitHub Wiki
Date: 2026-03-12
Status: Canonical storage and restore contract
Priority: Tier 3 / important stability contract
Related docs:
workbench_frame_tile_interaction_spec.mdpane_chrome_and_promotion_spec.md-
../../archive_docs/checkpoint_2026-04-02/graphshell_docs/implementation_strategy/workbench/2026-02-22_workbench_tab_semantics_overlay_and_promotion_plan.md— archived rollout record ../subsystem_storage/storage_and_persistence_integrity_spec.md../system/2026-03-12_workspace_decomposition_and_renaming_plan.md../../technical_architecture/2026-03-12_specification_coverage_register.md
This spec defines the persisted format and restore contract for named workbench frame snapshots.
It governs:
- the persisted bundle shape,
- pane identity rules inside a persisted frame,
- manifest/layout consistency requirements,
- stale-node and backward-compat recovery behavior,
- save, load, validate, repair, restore, and deletion semantics,
- the relationship between persisted frame bundles and graph-backed frame representation.
It does not govern:
- live tile-tree mutation semantics,
- tab-group creation/grouping semantics,
- command-surface flows that trigger save or restore,
- domain-graph persistence outside the frame bundle projection.
A named frame snapshot is a durable serialization of a workbench arrangement.
Normative rule:
- the persisted frame bundle is a durable storage format for workbench arrangement,
- it is not a second live workbench authority,
- runtime tile trees are restored from it, not mirrored continuously to it.
Related rule:
- frame bundles and graph-backed frame entities are distinct but coordinated artifacts,
- the bundle is the concrete layout/manifest format,
- the graph representation is the semantic persistence projection.
The canonical persisted object is PersistedFrame / PersistedWorkspace.
Current persisted top-level fields:
versionnamelayoutmanifest-
frame_tab_semantics(optional) metadataworkbench_profile
Current optional-field rule:
-
FrameTabSemanticsis now an optional additive field in the persisted bundle shape - bundle load/save must continue to work with or without
frame_tab_semanticspresent - semantic tab overlay remains frame state only; it is not graph WAL data
FrameLayout contains:
tree: Tree<PersistedPaneTile>
PersistedPaneTile currently allows:
-
Graph(legacy persisted graph-pane form kept for compatibility) Pane(PersistedPaneId)-
LegacyDiagnostic(deserialize-only backward compatibility)
Normative rule:
- layout stores structure and pane references,
- it does not store full pane payloads inline.
FrameManifest contains:
panes: BTreeMap<PaneId, PaneContent>member_node_uuids: BTreeSet<Uuid>
PaneContent currently allows:
GraphNodePane { node_uuid }Tool { kind }
Normative rule:
- the manifest is the canonical content map for pane ids referenced by layout,
- every
Pane(id)referenced in layout must resolve through manifest.
FrameMetadata contains:
created_at_msupdated_at_mslast_activated_at_ms
Normative rule:
- metadata is durable bookkeeping,
- it does not affect structural validity of the frame bundle.
FrameTabSemantics is an additive persisted frame-bundle field used to preserve semantic tab
membership independently from structural tile-tree normalization.
Normative rule:
-
FrameTabSemanticsis frame state only - it is additive to the persisted bundle and must remain backward compatible
- it does not belong in graph WAL / domain-graph persistence lanes
- consumers must tolerate the field being absent and fall back to deriving tab meaning from current persisted layout and manifest when needed
PersistedPaneId is a local serialized-pane identifier.
Normative rule:
- it is scoped only to a single persisted frame bundle,
- it is distinct from runtime
PaneId, - it must not be treated as a stable cross-bundle or live-runtime pane identity.
Node pane membership is persisted by node UUID, not runtime node key.
Normative rule:
- restore resolves UUIDs back into live node keys,
- stale node UUIDs may be dropped during restore/repair if they can no longer resolve,
- UUID is the durable node carrier for this format.
-
Graphpanes are persisted structurally as graph-pane placeholders and restore into a default graph-view payload. - tool panes persist by
ToolPaneStatekind, not by transient runtime handle identity.
validate_frame_bundle(...) is the canonical structural validator.
It must enforce:
- every layout-referenced
PaneIdexists in the manifest, - derived node membership from manifest equals declared
member_node_uuids.
Current canonical validation failures:
MissingManifestPaneMembershipMismatch
Normative rule:
- missing manifest pane is a hard structural error,
- membership mismatch is repairable if the rest of the bundle is structurally sound.
Current repairable condition:
- manifest membership mismatch.
Current repair action:
- recompute
member_node_uuidsfrom manifest pane content.
Normative rule:
- repair may normalize derived bookkeeping,
- repair must not silently invent missing panes or fabricate missing node bindings.
Future repair extensions must remain explicit and bounded.
The source of truth for save is the live runtime Tree<TileKind>.
Save sequence:
- convert runtime tree into persisted layout tree,
- derive manifest entries from runtime pane content,
- derive membership from manifest,
- carry forward compatible metadata when overwriting an existing named frame,
- write serialized JSON bundle,
- update graph-backed frame representation.
Normative rule:
- saving a frame snapshot must serialize from live workbench state,
- not from a stale cached layout mirror.
When overwriting an existing named frame:
-
created_at_msis preserved if valid, -
updated_at_msis refreshed, -
last_activated_at_msis preserved unless separately updated by activation flow.
Loading a named frame bundle must:
- retrieve serialized JSON by name,
- deserialize bundle,
- validate,
- repair membership mismatch if needed,
- fail on structural errors that cannot be repaired safely.
Restore must:
- convert the persisted layout/manifest back into a runtime
Tree<TileKind>, - resolve node UUIDs into current live
NodeKeyvalues, - drop or skip stale members that cannot be resolved,
- produce the restored runtime tree plus the set of restored nodes,
- preserve runtime validity even when some persisted members are stale.
Normative rule:
- restore is best-effort with respect to stale node resolution,
- but it must never produce an invalid runtime tree.
If restore yields an empty tree:
- the system may fall back to opening the routed request in the current frame,
- it must not silently claim success on an empty restored layout.
If restore fails:
- a warning/error path is emitted,
- routed open requests may fall back to current-frame open behavior,
- the failure must remain diagnosable.
Current explicit backward-compat path:
-
LegacyDiagnosticpersisted pane tiles are accepted during deserialize and mapped into the generic tool-pane path.
Normative rule:
- backward compatibility may preserve old persisted variants through deserialize-only aliases,
- current writers must not continue emitting obsolete variants once the new canonical form exists.
Named frame bundles are independently manageable persisted artifacts.
Supported lifecycle operations include:
- save/update named frame bundle,
- delete named frame bundle,
- prune empty named frame bundles,
- keep latest N named frame bundles by activation recency.
Normative rule:
- deletion removes the named persisted bundle,
- corresponding graph-backed frame projection should be removed or synchronized accordingly,
- pruning/retention behavior must operate on named persisted bundles, not on live runtime frames.
Authority split (updated 2026-03-21 per
2026-03-20_arrangement_graph_projection_plan.md):
-
Graph edges carry membership truth.
ArrangementRelation(FrameMember)andUserGroupededges are the durable, authoritative record of which nodes belong to which named frame. They persist in the graph store (redb WAL) independently of any frame bundle, survive restarts without a separate bootstrap step, and are the canonical input to graphlet computation. - FrameSnapshot captures workspace-restore state. The frame bundle records which nodes were warm/active at save time plus presentation state (active-tab identity, split geometry). Its role is workspace restore — re-opening the saved tiles and arrangement shape when a frame is loaded — not carrying membership truth. If the bundle is absent or stale, graph edges reconstruct durable membership without it.
- Neither should silently diverge. On save, graph membership and bundle membership must be mutually consistent. On load, graph edges take precedence for membership; bundle layout takes precedence for presentation shape.
Normative rule (revised):
- the FrameSnapshot bundle is the workspace-restore format for named frames,
- the graph-backed
FrameMemberedges are the authoritative membership record, - save must synchronize graph representation,
- delete must remove graph representation,
- activation should update activation metadata consistently.
Required coverage:
- save/load/restore roundtrip of a mixed frame,
- missing-manifest-pane validation failure,
- membership-mismatch repair,
- stale-node restore behavior,
- graph representation sync on save,
- graph representation deletion on bundle delete,
- empty restore fallback behavior,
- backward-compat deserialize of legacy diagnostic panes.
Required diagnostics:
- save failure,
- restore failure,
- stale-node pruning/repair,
- graph-sync failure where applicable.
- persisted frame bundle shape is treated as a canonical storage contract.
- layout and manifest roles remain distinct and explicit.
- validation and repair rules remain bounded and test-covered.
- restore never yields an invalid runtime tree.
- stale members are handled explicitly rather than by silent corruption.
- graph-backed frame projection remains synchronized with bundle lifecycle operations.