Skip to content

Release v2.48.0: Cloud conflict decisions save one note and stay saved, queued conflicts merge when they can, interrupted uploads clean up, Escape keeps Quick Capture open - #766

Merged
adibhanna merged 6 commits into
mainfrom
v2.48.0
Sep 11, 2026

Conversation

@adibhanna

@adibhanna adibhanna commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Release branch for ZenNotes 2.48.0: a follow-up to the 2.47.0 Cloud conflict work from Unyanda's reports, one Quick Capture fix, and a community Nix packaging update. No new features.

  • Resolving a conflict no longer depends on the rest of the vault. Choosing your version, the other device's version, both, or a combined note downloaded the full content manifest of the vault before it would save, so one small note waited on every unrelated asset and, on a vault with many attachments, could time out with nothing written. The server already checks the base revision and the destination path atomically, so the save now goes against the reviewed revision alone; taking the Cloud version, or keeping both, checks freshness from metadata and fetches only that file's retained bytes when the snapshot lacks them. Hosts without a revision reader keep the content-manifest path. A stale revision is reported as "The Cloud version changed. Sync again before choosing a version." Commit 06dc99e0.
  • A saved decision stays saved when the follow-up sync is slow or fails. The whole-vault sync after a decision used to own the outcome, leaving the review open on a resolved note and inviting a second save. The decision is retired the moment the save succeeds, the review advances or closes, the vault sync runs in the background, and a failure surfaces as Note saved. Remaining vault sync failed: plus the reason. A resolver that has saved ignores further clicks; Settings adopts the shared summary when the later run replaces it. Commit 06dc99e0.
  • Queued conflicts merge themselves once they safely can. A content conflict that could not merge when found stayed queued until opened, even once the two sides had become a clean three-way merge. Every sync now reconsiders those when the Cloud side is unchanged, all three paths agree, there is no draft and no paused path, and the current local file merges cleanly; the result is written and uploaded and the conflict clears. Overlaps, drafts, renames, paused paths and a file changing mid-write stay queued. Commit 06dc99e0.
  • Interrupted large-asset uploads close their file reader before the upload reservation is released, removing the stream-adapter path behind the "Controller is already closed" error, and Cloud Settings follows the live sync state (no stale "Everything is up to date" during a failure, no old error after a successful retry, drafts and unrelated backup or publishing errors stay visible). Commit 55cafa08.
  • Escape cancels a Quick Capture selection without closing the window (#765, uNyanda). An Escape the editor handles (Vim visual mode, insert mode, an ordinary selection, the completion popup, the picker) leaves the draft open; the next unhandled Escape still saves and hides, and the save shortcut is unchanged. Commit edd55e03.
  • Nix: the top-level X11 libraries instead of the deprecated xorg set (#763, blueagledev). Same derivation on the pinned nixpkgs; requires nixpkgs 25.11 or later. Commit ba4ae683.

Also on the branch: the release notes and post (443c34cd) and the chore(release): 2.48.0 version bump (71bafa15).

Verification

  • New tests: cloud-sync-resolution.test.ts, the "safely merging queued conflicts" block in cloud-sync-coordinator.test.ts, cloud-sync-upload-network.test.ts (real HTTP uploads of 8.1 MB and 10 MB with byte and hash checks, disconnects, timeouts, retries, restart recovery), resolver, Settings and status-store cases for a saved decision followed by a slow or failing sync, and the Quick Capture Escape cases in QuickCaptureApp.test.ts.
  • The Cloud fixes and the Quick Capture change were driven in the built desktop app on macOS against the local Cloud fixture. The Arch Linux upload report was reproduced through the synthetic upload fixtures only.
  • Release gates run fresh at the cut: apps/desktop build:prod (typecheck:node, typecheck:web, 749 tests, build, packaged CLI isolation) and electron-builder --dir, Go vet and tests (-count=1), app-core suite (2,003 tests), shared-domain suite (1,593 tests), repo typecheck, web build, and the signed packaged app launched from an isolated profile reaching a CDP page target in 3.3 s.
  • npm audit --omit=dev --audit-level=high passes; the only remaining finding is a moderate, development-only vitest advisory.
  • CI, CodeQL and the Nix build are green on ba4ae683.

Docs

Release notes and post under docs/releases/v2.48.0/; no in-app manual or website docs change was needed (bug fixes only). The website release page entry is prepared in the Laravel repo and is pushed with the release.

https://claude.ai/code/session_01Pa8u7FTkwZD3zZ5AoFxKC8

…ed conflicts merge when they can

Resolving a Cloud conflict downloaded the content manifest of the whole vault before it would save one note, so on a vault with many attachments the decision could time out with nothing written. The server already checks the base revision and the destination path atomically, and a vault-wide download cannot strengthen that check. Save against the reviewed revision alone; for the other device's version or keep-both, check freshness from metadata and fetch only that file's retained bytes when the snapshot lacks them. Hosts without a revision reader keep the content-manifest path.

The whole-vault sync that follows a decision used to own its outcome: a slow or failed run left the review open on a note that was already resolved and invited a second save. Retire the decision the moment the save succeeds, drop it from the queue, run the vault sync in the background, and report a failure as "Note saved. Remaining vault sync failed:" so the note is known to be safe. A resolver that has saved ignores further clicks. Settings adopts the shared summary when the later run replaces it.

A queued content conflict that could not merge when it was found stayed queued until opened, even once the two sides had become a clean three-way merge. Reconsider those on every sync when the Cloud side is unchanged, all three paths agree, there is no draft, and the current local file merges cleanly; write and upload the result and clear the conflict. Overlaps, drafts, renames, paused paths and a file changing mid-write stay queued for the user. Identical files keep the stricter convergence check from 2.47.0.

Claude-Session: https://claude.ai/code/session_01YK5tkUJtKXq3vh5mPmtcYT
Dispose native upload readers before reservation cleanup on failures and early responses. Keep Cloud settings aligned with shared sync progress, errors, and recovery without losing conflict drafts.

Add real HTTP upload, retry, restart, and UI status regression coverage.
Respect Escape events already handled by CodeMirror before saving or hiding Quick Capture. Preserve the subsequent unhandled Escape action and explicit save shortcut.

Cover Vim visual modes, insert mode, non-Vim selections, picker and completion dismissal, and save/close behavior.
blueagledev and others added 3 commits September 11, 2026 09:45
…org set (#763)

nixpkgs 26.05 deprecated the xorg package set in favour of the top-level
names, which exist from 25.11 on. Each xorg package now emits an
evaluation warning when instantiated, and package-desktop.nix uses 12 of
them; with allowAliases = false the set is absent and the file fails to
evaluate. This takes the same 12 libraries as top-level arguments
instead. The resulting derivation is unchanged: same drvPath before and
after on the flake's pinned nixpkgs.

Requires nixpkgs >= 25.11; drops 25.05, which has no lowercase top-level
libx11. That affects the copy-the-file route in packaging/nix/README.md
and flake consumers that make zennotes follow a 25.05 nixpkgs; the
flake's own lock is unaffected. 25.05 has been end-of-life since
2025-12-31.
Release notes and the social post for 2.48.0: the three Cloud conflict
fixes, the upload stream and sync status fixes, Escape cancelling a
Quick Capture selection (#765) and the Nix packaging update (#763).
No demo clip was recorded for this release.

Claude-Session: https://claude.ai/code/session_01Pa8u7FTkwZD3zZ5AoFxKC8
@adibhanna adibhanna changed the title Release 2.48.0: Cloud conflict decisions save one note, stay saved through a failed follow-up sync, and queued conflicts merge when they can Release v2.48.0: Cloud conflict decisions save one note and stay saved, queued conflicts merge when they can, interrupted uploads clean up, Escape keeps Quick Capture open Sep 11, 2026
@adibhanna
adibhanna merged commit 71bafa1 into main Sep 11, 2026
7 checks passed
@adibhanna
adibhanna deleted the v2.48.0 branch September 11, 2026 15:12
@adibhanna
adibhanna restored the v2.48.0 branch September 11, 2026 15:13
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