From eea3550859f0ba815cfc93b69d681636f2c72931 Mon Sep 17 00:00:00 2001 From: tomas Date: Thu, 26 Feb 2026 16:14:40 +0000 Subject: [PATCH 1/2] fix: Adjust metadata ID resolution order in Deepnote data handling Updated the order of ID resolution in the DeepnoteDataConverter, DeepnoteFileChangeWatcher, and Pocket modules to prioritize '__deepnoteBlockId' over 'id'. Added a test to ensure updates are not applied when cells lack block IDs and no fallback is available. --- .../deepnote/deepnoteDataConverter.ts | 2 +- .../deepnote/deepnoteFileChangeWatcher.ts | 2 +- .../deepnoteFileChangeWatcher.unit.test.ts | 27 +++++++++++++++++++ src/platform/deepnote/pocket.ts | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/notebooks/deepnote/deepnoteDataConverter.ts b/src/notebooks/deepnote/deepnoteDataConverter.ts index 115f4c75d1..9f71700a71 100644 --- a/src/notebooks/deepnote/deepnoteDataConverter.ts +++ b/src/notebooks/deepnote/deepnoteDataConverter.ts @@ -437,7 +437,7 @@ export class DeepnoteDataConverter { private createFallbackBlock(cell: NotebookCellData, index: number): DeepnoteBlock { const meta = cell.metadata as Record | undefined; - const preservedId = (meta?.id ?? meta?.__deepnoteBlockId ?? meta?.deepnoteBlockId) as string | undefined; + const preservedId = (meta?.__deepnoteBlockId ?? meta?.id ?? meta?.deepnoteBlockId) as string | undefined; const preservedSortingKey = (meta?.sortingKey ?? meta?.deepnoteSortingKey) as string | undefined; const preservedBlockGroup = meta?.blockGroup as string | undefined; diff --git a/src/notebooks/deepnote/deepnoteFileChangeWatcher.ts b/src/notebooks/deepnote/deepnoteFileChangeWatcher.ts index 5a292eddd4..b7da2080f3 100644 --- a/src/notebooks/deepnote/deepnoteFileChangeWatcher.ts +++ b/src/notebooks/deepnote/deepnoteFileChangeWatcher.ts @@ -520,7 +520,7 @@ export class DeepnoteFileChangeWatcher implements IExtensionSyncActivationServic } private getBlockIdFromMetadata(metadata: Record | undefined): string | undefined { - return (metadata?.id ?? metadata?.__deepnoteBlockId) as string | undefined; + return (metadata?.__deepnoteBlockId ?? metadata?.id) as string | undefined; } private handleFileChange(uri: Uri): void { diff --git a/src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts b/src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts index a2ba5af5b3..0a7ccc1787 100644 --- a/src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts +++ b/src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts @@ -1052,6 +1052,33 @@ project: execOnDidCreate.dispose(); }); + test('should not apply updates when cells have no block IDs and no fallback', async () => { + const snapshotUri = Uri.file('/workspace/snapshots/my-project_project-1_latest.snapshot.deepnote'); + const notebook = createMockNotebook({ + uri: Uri.file('/workspace/test.deepnote'), + cells: [ + { + metadata: {}, + outputs: [], + kind: NotebookCellKind.Code, + document: { getText: () => 'print("hello")' } + } + ] + }); + + when(mockedVSCodeNamespaces.workspace.notebookDocuments).thenReturn([notebook]); + + snapshotOnDidChange.fire(snapshotUri); + await new Promise((resolve) => setTimeout(resolve, debounceWaitMs)); + + assert.isAtLeast(readSnapshotCallCount, 1, 'readSnapshot should be called'); + assert.strictEqual( + snapshotApplyEditCount, + 0, + 'applyEdit should NOT be called when no block IDs can be resolved' + ); + }); + test('should fall back to replaceCells when no kernel is active', async () => { const fbDisposables: IDisposableRegistry = []; const fbOnDidChange = new EventEmitter(); diff --git a/src/platform/deepnote/pocket.ts b/src/platform/deepnote/pocket.ts index 783cfdc28d..1bf7b6286c 100644 --- a/src/platform/deepnote/pocket.ts +++ b/src/platform/deepnote/pocket.ts @@ -76,7 +76,7 @@ export function createBlockFromPocket(cell: NotebookCellData, index: number): De const metadata = cell.metadata ? { ...cell.metadata } : undefined; // Get id from top-level metadata before cleaning it up // Check both 'id' and backup '__deepnoteBlockId' in case VS Code modifies 'id' - const cellId = (metadata?.id as string | undefined) || (metadata?.__deepnoteBlockId as string | undefined); + const cellId = (metadata?.__deepnoteBlockId as string | undefined) || (metadata?.id as string | undefined); logger.debug( `[Pocket] createBlockFromPocket index=${index}: cell.metadata.id=${metadata?.id}, __deepnoteBlockId=${metadata?.__deepnoteBlockId}, using cellId=${cellId}, metadata keys=${ From 4577bc983a405dfdeb98e85499b923c00c9b0c52 Mon Sep 17 00:00:00 2001 From: tomas Date: Thu, 26 Feb 2026 17:06:56 +0000 Subject: [PATCH 2/2] test: Update snapshot reading logic in deepnoteFileChangeWatcher unit tests Replaced the setTimeout with a waitFor function to ensure that readSnapshot is called at least once after the snapshot change event is fired. This improves the reliability of the test by directly waiting for the expected condition. --- src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts b/src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts index 0a7ccc1787..c9229820e2 100644 --- a/src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts +++ b/src/notebooks/deepnote/deepnoteFileChangeWatcher.unit.test.ts @@ -1069,7 +1069,8 @@ project: when(mockedVSCodeNamespaces.workspace.notebookDocuments).thenReturn([notebook]); snapshotOnDidChange.fire(snapshotUri); - await new Promise((resolve) => setTimeout(resolve, debounceWaitMs)); + + await waitFor(() => readSnapshotCallCount >= 1); assert.isAtLeast(readSnapshotCallCount, 1, 'readSnapshot should be called'); assert.strictEqual(