Repository navigation
ZenNotes 2.69.1 fixes renaming on Windows: renaming a note or an attachment to a name another one already has could replace that other file. It always stops and says the name is taken now. Assistants connected through MCP see the tasks in numbered lists and quotes that the app lists, and converting a table to a database in a long note offers the right name.
Released from 86e98b49 through #936 on 2026-10-13. All installers are verified.
Fixes
-
A rename onto a name that is taken never replaces the other note (Windows). Before: on Windows, renaming a note or an attachment to the name of another one in the same folder could go through and replace that other file, instead of saying the name is taken. An assistant renaming a note through MCP could do the same. ZenNotes lets a rename through onto an existing name only when both names reach the same file, which is how a rename that only changes capitals looks on a disk that ignores case. It asked that by comparing file ids as JavaScript numbers, and a Windows file id is 64 bits: two files saved one after the other could round to the same number and read as one file. It compares the whole id now, as Cloud sync already did. On macOS and Linux file ids stay small enough not to round. The check came with 2.55.0 (#839), so every version since then could do this on Windows.
-
Converting a table offers the heading right above it, in a long note too. Before: in a long note, Convert Table to Database could offer the name of an earlier heading when the editor had not yet read as far as the table. The name could be edited before saving, so nothing was saved under it unseen. It now reads the note up to the table before choosing.
-
Assistants see tasks in numbered lists and quotes. Before: an AI assistant connected through MCP saw only bullet tasks.
list_tasksskipped tasks in numbered lists (1. [ ],1) [ ]) and in quotes (> - [ ]), andtoggle_taskcould not reach them, although the app's Tasks view lists them; a task below one of them also got a different number than the app gives it. The MCP server now reads task lines the way the app does.
For contributors
apps/desktop/src/main/same-file.ts(6d750154):fileIdentity()is a bigint stat andisSameFile()compares its device and inode. Every "is the destination the file itself?" question goes through it: note, asset and folder renames invault.ts, the shared sidecar mover innote-sidecars.ts, a workflow rename's cleanup inworkflows.ts(which kept the old file instead of removing it) and the MCP server'srenameNote.windows-file-ids.testing.tsgives tests the ids Node reports on Windows: the full id from a bigint stat, a rounded number otherwise. Six tests use it, for desktop and MCP note and folder renames, an asset rename and a workflow rename; each failed on the old guards with the same symptom CI showed.headingAbove()intable-to-database.tsasksensureSyntaxTree(state, pos, 50)whether the parse reached the table (ba74c54f):noteMarkdown's outer tree spans the whole note even while its body parse is short, sosyntaxTree(state).lengthalways said yes. Its test cuts the first parse short with a clock that moves 30 ms per read.- Five CI failures since September are fixed at their causes (
65d30daf): tests that readsyntaxTree(state)after a 20 ms parse budget (task rollups, incremental parsing) or before the first parse reached a code block (search highlighting), two timeouts for a cold import and a slow runner, and a port race intest-app-core-browser.mjsthat gave the API server and Chrome the same port, the Ubuntu "Exercise the installed editor in Chrome" failure. That step then failed once on this release's PR for a second reason, read before re-running: a cold Chrome on a busy runner took 26 seconds to open DevTools against a 30 second wait; it gets 90 now (86e98b49). - The MCP server's
TASK_LINE_REmatches the lines@shared/tasklists' does (a2b4bfe7); a test pins that parity on a note with every shape. zn 0.7.2 (zn taskandzn mcp) and server 2.69.1 (the web app's Tasks view), released the same evening, read the same lines; the release runbook now checks every copy of this regex across the three repos before a cut. - The Release workflow starts on tags matching
v[0-9]*instead ofv*(c35ae438). Publishing a boundary package's draft creates a tag such asviewer-2.69.0-viewer.h5e7ef0f99f145d0a, and from 2.63.0 to 2.69.0 every viewer publish built a whole desktop release and uploaded it onto the viewer pre-release. The desktop releases themselves were never touched.
Verification
How to test locally:
- The guards, on any OS:
cd apps/desktop && npx vitest run src/main/note-sidecars.test.ts src/main/vault.test.ts src/main/workflows.test.ts -t "Windows file id|round to one number|rounds to the same number". Six tests pass. Inapps/desktop/src/main/same-file.ts, drop{ bigint: true }fromfileIdentity(the old number compare) and run them again: all six fail, the rename resolving instead of refusing. - On Windows, in a vault with two notes
OneandTwoin one folder, renameOnetoTwo. After: "A note named “Two” already exists in this folder", and both notes keep their text. Before, on a disk whose file ids round together,Twocould end up holdingOne's text; on a fresh Windows install the ids are small and it would not happen. - Convert Table to Database in a long note: put
# Earlyat the top of a note, about fifty paragraphs, then# Roadmapand a table under it. Open the note and right away choose Convert Table to Database from the table's menu. After: the name offered is "Roadmap". The testnpx vitest run src/lib/table-to-database.test.tsinpackages/app-corecovers it with a slow clock. - Tasks for an assistant: in a note with
1. [ ] numberedand> - [ ] quoted, ask an assistant connected through ZenNotes' MCP server to list your open tasks. Before: neither appeared. After: both do, and it can check them off;cd apps/desktop && npx vitest run src/mcp/vault-ops.test.ts -t "ordered"covers it. - A rename that only changes capitals still works: rename
Onetooneon macOS or Windows. The note is renamed in place, and its comments come along.
Distribution channels
- GitHub release: installers for macOS (Apple silicon and Intel DMG and zip), Windows (x64 and ARM64 installer and zip) and Linux (x64 and arm64 AppImage, deb, rpm, pacman, tar.gz), with five update manifests.
- AUR:
zennotes-bin2.69.1-1 (AUR clone4dc9727, repo9b754de5), pinned toZenNotes-2.69.1-linux-x64.tar.gzSHA-256e0077164…b4e3(GitHub's own digest for the asset), after the bump script checked the bundled CLI folder is world-readable; aur-check passed. - Nix:
packaging/nix/release-data.jsonlifted from the update workflow (run 38456877828, bot PR #937 closed) asdde7064d; itsdesktopHashis the same digest as the AUR pin, and nix-build on main passed. - nixpkgs: NixOS/nixpkgs#572180, still waiting for review, now proposes 2.64.0 -> 2.69.1 (
17d94f17); the source hash comes from the tag tarball, by the same prefetch that reproduces the 2.64.0 hash in nixpkgs today. - Homebrew:
zennotes2.69.1 in the tap (4cf0b75), mirrored in5e01d4f4; both DMGs were downloaded and their SHA-256 matches the cask and GitHub's digest;brew styleclean. - Website: ZenNotes/website#78 merged at
d8718ebewith the 2.69.1 entry (three fixes); Laravel Cloud deployed it and the live check passed: /releases leads with 2.69.1 and all three fixes, and page 2 starts at 2.66.0.npm run verify:channels -- 2.69.1: every applicable channel serves v2.69.1. - zn CLI: 0.7.2 (ZenNotes/tui#9, tag
v0.7.2, Homebrew formula8744800, tapebf346c):zn taskandzn mcplist tasks in numbered lists and quotes, as the desktop's MCP server does in this release. A zn installed from Settings → CLI updates itself; the copy bundled in the app stays 0.7.1 until the next desktop release. - Server: ZenNotes/znserver v2.69.1 lists the same task lines in the web app's Tasks view and pins
web-2.69.1-web.he3742063f1986fdb, built from this release's commit (#28); its five binaries matchSHA256SUMS, and the released darwin-arm64 binary reports v2.69.1, serves the app, answers 401 without the token and lists all five tasks of the test note with it. Dockeradibhanna/zennotes:2.69.1,2.69andlatest(linux/amd64 and arm64, one digestdb6ced3f…). - Phones: unchanged; iPhone 1.25.0 and Android 1.1.39 run
core-2.69.0.
Release validation
Every CI failure on main since September was read before this release. None was a product bug: seven were fixed already or were runner infrastructure, five were test or harness bugs, fixed in 65d30daf, and reading one of them found the table naming bug. The Windows rename bug was found like this: after 2.69.0 was tagged, the windows-11-arm CI job on main failed once in vault.test.ts ("says when another note already has the name": the rename resolved instead of refusing), on code the release PR had passed on both Windows runners. The release process now reads every CI failure since the previous release before the cut and scans the tree for code patterns that already shipped a bug; on this branch both are clean: the two failures since 2.69.0 are this bug and 16 tests in unrelated suites timing out at 5 seconds on one slow windows-latest runner, and the scan, which flags the six old guards on 2.69.0's code, finds nothing.
Release-time gates on a2b4bfe7 (the release commit 86e98b49 adds only the browser check's longer Chrome wait): typecheck 8/8 and test:run 6/6 with no turbo cache (shared-domain 1,994, app-core 3,221, quicklook 15, desktop 1,147), fresh build:prod green, npm audit --omit=dev --audit-level=high clean. Pack: Developer ID signed, deep strict codesign OK, bundled zn v0.7.1 in a drwxr-xr-x Resources/zn-cli, the Quick Look extension; packaged launch check: page target and drawn app in 1.5 s, version 2.69.1. Smoke suites vim-editor (12), sidebar-vim (12) and editor-improvements (19) pass on the first run. ci-sweep.py v2.69.0: two failures, both explained; hazard-scan.py --since v2.69.0: clean. Release PR #936: all 10 checks green on 86e98b49, including the windows-11-arm package and launch check. The run before it, on a2b4bfe7, failed once in Ubuntu's Chrome step and was read before anything else: the ports were distinct, and a cold Chrome took 26 seconds against a 30 second wait, fixed in 86e98b49. CI on main for 86e98b49 (run 38452074217) passed all six jobs before the tag, and again for the packaging commits (dde7064d). Release run 38455066243: all six jobs green in about 45 minutes; verify-installers.py ALL GOOD on the five manifests; both DMGs hash to GitHub's digests and the cask, and the app in each is Notarized Developer ID with a valid stapled ticket. npm run verify:channels -- 2.69.1: every channel serves v2.69.1. What's New on the update from 2.69.0, in the built app over CDP: "What's new in ZenNotes 2.69.1" with the Renames on Windows highlight.