Skip to content

fix(@deepnote/cli): timestamped snapshots#320

Merged
dinohamzic merged 2 commits intomainfrom
cli-timestamped-snapshots
Feb 23, 2026
Merged

fix(@deepnote/cli): timestamped snapshots#320
dinohamzic merged 2 commits intomainfrom
cli-timestamped-snapshots

Conversation

@dinohamzic
Copy link
Contributor

@dinohamzic dinohamzic commented Feb 23, 2026

Screenshot 2026-02-23 at 12 07 10

Summary by CodeRabbit

Release Notes

  • New Features

    • Snapshot functionality now creates both timestamped and latest snapshot files, enabling better data preservation and historical tracking.
  • Tests

    • Updated test coverage to verify creation and validation of both timestamped and latest snapshot files.

@dinohamzic dinohamzic self-assigned this Feb 23, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

📝 Walkthrough

Walkthrough

The changes introduce timestamped snapshot file generation to the snapshot persistence mechanism. A new timestampedSnapshotPath field is added to SaveSnapshotResult. The saveExecutionSnapshot function now writes snapshots to a timestamped filename first, then copies the file to the latest snapshot path. This two-step approach reduces corruption risk. Tests are updated across multiple files to verify both snapshot files are created with correct content and formatting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Updates Docs ⚠️ Warning PR introduces timestamped snapshot feature but lacks documentation updates in CLI README or similar files. Update packages/cli/README.md to document snapshot file locations, timestamped vs latest snapshots, and access methods.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly describes the main change: implementing timestamped snapshots in the CLI. It accurately reflects the core functionality added across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Feb 23, 2026

Codecov Report

❌ Patch coverage is 58.82353% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.43%. Comparing base (13427a5) to head (23b8306).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
packages/mcp/src/tools/execution.ts 0.00% 7 Missing ⚠️

❌ Your patch status has failed because the patch coverage (58.82%) is below the target coverage (80.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #320      +/-   ##
==========================================
- Coverage   83.47%   83.43%   -0.04%     
==========================================
  Files         114      114              
  Lines        6971     6980       +9     
  Branches     1864     1864              
==========================================
+ Hits         5819     5824       +5     
- Misses       1152     1156       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/cli/src/commands/run.test.ts (1)

67-69: 🧹 Nitpick | 🔵 Trivial

Initial mock missing timestampedSnapshotPath.

Overwritten in beforeEach, but inconsistent with SaveSnapshotResult type.

 const mockSaveExecutionSnapshot: MockedFunction<typeof saveExecutionSnapshot> = vi
   .fn()
-  .mockResolvedValue({ snapshotPath: '/mock/snapshot.snapshot.deepnote' })
+  .mockResolvedValue({
+    snapshotPath: '/mock/snapshot.snapshot.deepnote',
+    timestampedSnapshotPath: '/mock/snapshot-timestamped.snapshot.deepnote',
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/cli/src/commands/run.test.ts` around lines 67 - 69, The mock for
saveExecutionSnapshot (mockSaveExecutionSnapshot) returns an object missing
timestampedSnapshotPath which violates the SaveSnapshotResult shape; update the
mockResolvedValue to include timestampedSnapshotPath (alongside snapshotPath) so
the initial mock matches the SaveSnapshotResult type used elsewhere (and remains
consistent with the value overwritten in beforeEach).
packages/mcp/src/tools/execution.ts (1)

248-298: ⚠️ Potential issue | 🟠 Major

Return type omits timestampedSnapshotPath.

Writes both snapshots but returns only snapshotPath. CLI version returns both. Inconsistent API.

-): Promise<{ snapshotPath: string }> {
+): Promise<{ snapshotPath: string; timestampedSnapshotPath: string }> {

And at line 298:

-  return { snapshotPath }
+  return { snapshotPath, timestampedSnapshotPath }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mcp/src/tools/execution.ts` around lines 248 - 298, The function
currently returns Promise<{ snapshotPath: string }> but writes both
timestampedSnapshotPath and snapshotPath; update the return type to include
timestampedSnapshotPath (e.g. Promise<{ snapshotPath: string;
timestampedSnapshotPath: string }>) and change the final return to return both {
snapshotPath, timestampedSnapshotPath } (symbols: snapshotPath,
timestampedSnapshotPath, serializeDeepnoteSnapshot, generateSnapshotFilename);
also search for and update any callers expecting the old single-field return to
handle the new two-field object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/cli/src/commands/run.test.ts`:
- Around line 67-69: The mock for saveExecutionSnapshot
(mockSaveExecutionSnapshot) returns an object missing timestampedSnapshotPath
which violates the SaveSnapshotResult shape; update the mockResolvedValue to
include timestampedSnapshotPath (alongside snapshotPath) so the initial mock
matches the SaveSnapshotResult type used elsewhere (and remains consistent with
the value overwritten in beforeEach).

In `@packages/mcp/src/tools/execution.ts`:
- Around line 248-298: The function currently returns Promise<{ snapshotPath:
string }> but writes both timestampedSnapshotPath and snapshotPath; update the
return type to include timestampedSnapshotPath (e.g. Promise<{ snapshotPath:
string; timestampedSnapshotPath: string }>) and change the final return to
return both { snapshotPath, timestampedSnapshotPath } (symbols: snapshotPath,
timestampedSnapshotPath, serializeDeepnoteSnapshot, generateSnapshotFilename);
also search for and update any callers expecting the old single-field return to
handle the new two-field object.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 13427a5 and 7cf4901.

📒 Files selected for processing (4)
  • packages/cli/src/commands/run.test.ts
  • packages/cli/src/utils/output-persistence.test.ts
  • packages/cli/src/utils/output-persistence.ts
  • packages/mcp/src/tools/execution.ts

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 23, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/convert/src/snapshot/save.ts`:
- Around line 102-113: Validate timing.finishedAt before calling toISOString in
save.ts: check that new Date(timing.finishedAt) is a valid Date (not Invalid
Date) and if it is invalid, either throw a clear error or fall back to a safe
timestamp (e.g., Date.now() or timing.startedAt) before constructing timestamp
(the variable assigned from toISOString) and calling
generateSnapshotFilename(slug, file.project.id, timestamp); ensure the chosen
fallback flows into timestampedFilename/timestampedSnapshotPath and the writes
that follow so snapshot saving does not throw unexpectedly.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7cf4901 and d0fb902.

📒 Files selected for processing (5)
  • packages/cli/src/utils/output-persistence.ts
  • packages/convert/src/index.ts
  • packages/convert/src/snapshot/index.ts
  • packages/convert/src/snapshot/save.ts
  • packages/mcp/src/tools/execution.ts

@dinohamzic dinohamzic force-pushed the cli-timestamped-snapshots branch from d0fb902 to 7cf4901 Compare February 23, 2026 11:38
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 23, 2026
@dinohamzic dinohamzic marked this pull request as ready for review February 23, 2026 12:24
@dinohamzic dinohamzic requested a review from a team as a code owner February 23, 2026 12:24
@dinohamzic dinohamzic requested a review from Artmann February 23, 2026 12:24
@Artmann
Copy link
Contributor

Artmann commented Feb 23, 2026

Seems to work well
CleanShot 2026-02-23 at 13 27 35

// Serialize and write both snapshot files
const snapshotYaml = serializeDeepnoteSnapshot(snapshot)
await fs.writeFile(timestampedSnapshotPath, snapshotYaml, 'utf-8')
await fs.writeFile(snapshotPath, snapshotYaml, 'utf-8')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of writing directly to the latest snapshot, we should finish writing the timestamped file, and copy it to the latest path. That way we reduce the chance of corrupting the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, addressed 👌

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/mcp/src/tools/execution.ts (2)

243-298: 🧹 Nitpick | 🔵 Trivial

Duplicated snapshot logic.

This function mirrors saveExecutionSnapshot in packages/cli/src/utils/output-persistence.ts. Consider importing or consolidating to avoid drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mcp/src/tools/execution.ts` around lines 243 - 298, The
saveExecutionSnapshot implementation is duplicated; refactor to reuse the
existing implementation in packages/cli/src/utils/output-persistence.ts by
extracting the shared snapshot logic into a single shared utility (or directly
importing the existing saveExecutionSnapshot/exported helpers) and update this
file to call that shared function; specifically consolidate behaviors around
building outputsByBlockId, merging outputs into DeepnoteFile, using
splitDeepnoteFile, getSnapshotDir, slugifyProjectName, generateSnapshotFilename
and serializeDeepnoteSnapshot so this module only calls the shared routine
instead of duplicating the logic.

243-298: ⚠️ Potential issue | 🟡 Minor

Return type inconsistent with output-persistence.ts.

timestampedSnapshotPath is computed but not returned. The sibling implementation in packages/cli/src/utils/output-persistence.ts returns both paths in SaveSnapshotResult.

Proposed fix
 async function saveExecutionSnapshot(
   sourcePath: string,
   file: DeepnoteFile,
   blockOutputs: Array<{ id: string; outputs: unknown[]; executionCount?: number | null }>,
   timing: { startedAt: string; finishedAt: string }
-): Promise<{ snapshotPath: string }> {
+): Promise<{ snapshotPath: string; timestampedSnapshotPath: string }> {
   // ... existing code ...

-  return { snapshotPath }
+  return { snapshotPath, timestampedSnapshotPath }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/mcp/src/tools/execution.ts` around lines 243 - 298, The function
saveExecutionSnapshot currently computes timestampedSnapshotPath but only
returns snapshotPath, causing an inconsistent return type versus the sibling
SaveSnapshotResult; change the function's return type (Promise<{ snapshotPath:
string }>) to include timestampedSnapshotPath (e.g. Promise<{ snapshotPath:
string; timestampedSnapshotPath: string }>), and update the final return to
return both { snapshotPath, timestampedSnapshotPath }. Update any callers or
exported type aliases (e.g., SaveSnapshotResult) to accept the new shape so
usages of saveExecutionSnapshot receive both paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/mcp/src/tools/execution.ts`:
- Around line 243-298: The saveExecutionSnapshot implementation is duplicated;
refactor to reuse the existing implementation in
packages/cli/src/utils/output-persistence.ts by extracting the shared snapshot
logic into a single shared utility (or directly importing the existing
saveExecutionSnapshot/exported helpers) and update this file to call that shared
function; specifically consolidate behaviors around building outputsByBlockId,
merging outputs into DeepnoteFile, using splitDeepnoteFile, getSnapshotDir,
slugifyProjectName, generateSnapshotFilename and serializeDeepnoteSnapshot so
this module only calls the shared routine instead of duplicating the logic.
- Around line 243-298: The function saveExecutionSnapshot currently computes
timestampedSnapshotPath but only returns snapshotPath, causing an inconsistent
return type versus the sibling SaveSnapshotResult; change the function's return
type (Promise<{ snapshotPath: string }>) to include timestampedSnapshotPath
(e.g. Promise<{ snapshotPath: string; timestampedSnapshotPath: string }>), and
update the final return to return both { snapshotPath, timestampedSnapshotPath
}. Update any callers or exported type aliases (e.g., SaveSnapshotResult) to
accept the new shape so usages of saveExecutionSnapshot receive both paths.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d0fb902 and 23b8306.

📒 Files selected for processing (2)
  • packages/cli/src/utils/output-persistence.ts
  • packages/mcp/src/tools/execution.ts

@dinohamzic dinohamzic merged commit 0e8ac2f into main Feb 23, 2026
20 of 21 checks passed
@dinohamzic dinohamzic deleted the cli-timestamped-snapshots branch February 23, 2026 12:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants