From 5148168c2616713febd2895a7a169dd0d08ef243 Mon Sep 17 00:00:00 2001 From: youzini Date: Sat, 30 May 2026 18:11:00 +0000 Subject: [PATCH] feat(persistence): wire upgrade-on-read into snapshot load --- sync/bme-db.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/sync/bme-db.js b/sync/bme-db.js index fe3d279..11f92c9 100644 --- a/sync/bme-db.js +++ b/sync/bme-db.js @@ -1,4 +1,9 @@ import { createEmptyGraph, deserializeGraph } from "../graph/graph.js"; +import { upgradeGraphSnapshotOnRead } from "./graph-snapshot-upgrade.js"; +import { + GRAPH_SNAPSHOT_SCHEMA_VERSION, + readGraphSnapshotSchemaVersion, +} from "./graph-snapshot-schema.js"; import { hasMeaningfulMemoryScope, normalizeMemoryScope, @@ -2917,6 +2922,34 @@ export function buildPersistDelta(beforeSnapshot, afterSnapshot, options = {}) { export function buildGraphFromSnapshot(snapshot, options = {}) { const shouldCollectDiagnostics = typeof options?.onDiagnostics === "function"; const hydrateStartedAt = shouldCollectDiagnostics ? readPersistDeltaNow() : 0; + // Upgrade-on-read hook. Only a durable snapshot (has explicit meta/nodes shape) + // with a layout schemaVersion OLDER than current is upgraded in place. We swap + // the snapshot only when an actual upgrade step ran, so unknown top-level keys + // (e.g. __stBmeTombstonesOmitted) and runtime-graph-shaped inputs pass through + // untouched. At GRAPH_SNAPSHOT_SCHEMA_VERSION=1 with an empty step map this is a + // strict no-op; it becomes live behavior at the first future schema bump. + let snapshotSchemaUpgradeInfo = null; + if ( + snapshot && + typeof snapshot === "object" && + !Array.isArray(snapshot) && + Array.isArray(snapshot.nodes) + ) { + const storedVersion = readGraphSnapshotSchemaVersion(snapshot); + if (storedVersion > 0 && storedVersion < GRAPH_SNAPSHOT_SCHEMA_VERSION) { + const upgradeResult = upgradeGraphSnapshotOnRead(snapshot); + if (upgradeResult.upgraded) { + snapshot = upgradeResult.snapshot; + } + snapshotSchemaUpgradeInfo = { + from: upgradeResult.fromVersion, + to: upgradeResult.toVersion, + upgraded: upgradeResult.upgraded, + ahead: upgradeResult.ahead, + steps: upgradeResult.steps, + }; + } + } const hydrateDiagnostics = shouldCollectDiagnostics ? { success: false, @@ -3379,6 +3412,7 @@ export function buildGraphFromSnapshot(snapshot, options = {}) { ...hydrateDiagnostics, success: true, integrityReasons: [], + snapshotSchemaUpgrade: snapshotSchemaUpgradeInfo, totalMs: readPersistDeltaNow() - hydrateStartedAt, }); }