Skip to content

feat(runtimed): Phase 1.4 - delegate save-to-disk to daemon#545

Merged
rgbkrk merged 5 commits intomainfrom
quill/phase-1.4-delegate-save-to-daemon
Mar 5, 2026
Merged

feat(runtimed): Phase 1.4 - delegate save-to-disk to daemon#545
rgbkrk merged 5 commits intomainfrom
quill/phase-1.4-delegate-save-to-daemon

Conversation

@rgbkrk
Copy link
Member

@rgbkrk rgbkrk commented Mar 5, 2026

Summary

Moves notebook save-to-disk from the Tauri process to the daemon, eliminating the last major NotebookState consumer. The daemon already owns the canonical Automerge document, making it the ideal place to serialize notebooks to disk.

Key changes:

  • Add optional path parameter to SaveNotebook protocol request
  • Daemon returns absolute path in NotebookSaved response
  • Remove local NotebookState::serialize() fallback — daemon save is now required
  • Add save(path=None) to Python bindings for MCP server use

Implementation details

  • save_notebook: Requires daemon connection; sends SaveNotebook { path: None } to use room's notebook_path
  • save_notebook_as: Sends SaveNotebook { path: Some(new_path) } to daemon, handles path updates and sync reconnection
  • Refactored save_notebook_to_disk() to accept optional target path and return the saved path
  • Python bindings follow existing async/sync patterns in AsyncSession and Session

Verification

  • cargo fmt — Rust formatting
  • npx @biomejs/biome check --fix — TypeScript/JavaScript linting
  • cargo clippy --all-targets -- -D warnings — No clippy warnings
  • cargo test --verbose — All 171 tests pass
  • Protocol serialization tests pass

Reviewer checklist:

  • Verify save operation works for existing notebooks (Cmd+S)
  • Verify save-as operation creates file at new path and reconnects sync
  • Verify Python session.save(path) returns absolute path
  • Verify error handling when daemon is disconnected

PR submitted by @rgbkrk's agent, Quill

rgbkrk added 5 commits March 5, 2026 14:08
Part of Phase 1.4 - delegate save-to-disk to daemon.

- Add `path: Option<String>` to `SaveNotebook` request
- Add `path: String` to `NotebookSaved` response (returns saved path)
- Refactor `save_notebook_to_disk()` to accept optional target path
- Add `save(path=None)` to AsyncSession and Session Python bindings

When `path` is provided, daemon saves to that path (with .ipynb appended
if needed, converted to absolute). When `path` is None, saves to the
room's original notebook_path.
Part of Phase 1.4 - daemon save is now required.

- Remove local fallback from save_notebook - daemon save is now required
- Update save_notebook_as to use daemon with path parameter
- Remove NotebookState::serialize() calls from save commands
- Daemon must be connected for save operations to succeed
Address reviewer feedback on PR #545:

- 🔴 Format-on-save now writes to Automerge (not NotebookState)
  - Added format_notebook_cells() in daemon using ruff/deno fmt
  - Daemon updates Automerge doc with formatted sources
  - Removed client-side formatting loop from save_notebook/save_notebook_as
  - Clients send format_cells: true, receive synced formatted sources

- High: Save As now uses daemon-returned path
  - Use saved_path from NotebookSaved response for nb.path, ctx.path, window title
  - Handles daemon normalizations (e.g., appending .ipynb extension)

- Medium: Reject relative paths in daemon
  - Return error instead of joining with unpredictable daemon CWD
  - Clients (Tauri file dialog, Python SDK) should provide absolute paths
Address additional reviewer feedback:

- Skip formatting for unknown kernelspec instead of defaulting to Python
  - Prevents incorrectly reformatting non-Python/Deno notebooks
  - Logs info message when skipping

- Add tests for new save_notebook_to_disk behavior:
  - test_save_notebook_to_disk_with_target_path: absolute path saves
  - test_save_notebook_to_disk_appends_ipynb_extension: extension handling
  - test_save_notebook_to_disk_rejects_relative_path: error on relative
  - test_format_notebook_cells_skips_unknown_runtime: format skip behavior
The trust signature was being lost during Save As because it wasn't
part of the typed RuntMetadata struct. When saving to a new path,
the typed NotebookMetadataSnapshot serialization would omit trust
fields that existed in the raw Automerge JSON.

Adding trust_signature and trust_timestamp as optional fields to
RuntMetadata ensures they are properly serialized/deserialized and
preserved during Save As operations.

This fixes notebooks showing as "Untrusted" after Save As even when
the user had already approved the dependencies.
@rgbkrk rgbkrk marked this pull request as ready for review March 5, 2026 23:17
@rgbkrk rgbkrk merged commit a5491a0 into main Mar 5, 2026
23 of 24 checks passed
@rgbkrk rgbkrk deleted the quill/phase-1.4-delegate-save-to-daemon branch March 5, 2026 23:57
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.

1 participant