OpenKnowledge
Changelog

v0.63.6

Released Aug 26, 2026.

Patch Changes

  • A document holding a component the editor does not recognize inside another component was reported as losing content when nothing had been lost.

    When the editor meets a component it does not recognize, it shows that component as its raw source text. Before persisting a document the server compares the version it is about to write against a fresh reading of those same bytes, so that a write which drops your writing gets caught. That comparison could not tell raw source text apart from the structure the very same bytes read back as, so it counted the tags and the markdown inside a raw view as writing that had gone missing. Documents in that shape were flagged, and left rows in their own history announcing a recovered loss that never happened.

    The comparison now reads a raw-source block as source rather than as writing, so it no longer expects that block's markup to survive being parsed. Documents in that shape are no longer flagged for it.

    What this gives up: this check gets less sensitive, not just more accurate. Writing that vanishes from inside a raw-source block is no longer visible to it. Neither, in a document holding a raw-source block, is some writing that vanishes from outside one: the check compares two whole documents as a single run of letters with no way to tell which letters came from which block, so once a raw-source block is set aside its letters can stand in for writing lost nearby. What that costs is this check's warning and the restore point it saves before applying such a write. What bounds it is where the check started: before this change it flagged every save of a document holding an unrecognized component, so what it reported for those documents was already unusable.

    Unchanged: when a wrapper the editor does recognize, such as a callout, an accordion or a set of tabs, holds a component it does not recognize, blank lines you wrote around that wrapper are still rewritten. That is tracked separately.

  • An unsynced edit can no longer cross between two open projects. When a window reconnects to a server that has restarted, it parks any edit that had not yet reached the server in browser storage and replays it once the connection is healthy again. That parking spot was named after the branch and the document path, but not after the project, and every project window in the desktop app is served from a single origin. So two projects that are worktrees of the same repository, open on the same branch, addressed one parking spot for a document at the same path. Whichever window replayed first consumed the record, which meant a project could receive text that was typed in a different project, while the window that lost the race stood down and dropped its own edit. Because the payload is document content rather than a claim about state, this crossed edits silently instead of raising an error. The parking spot is now named per project as well, so a window only ever contends with other windows of the same project.

  • Removed the transitional ui.lock cleanup that shipped alongside the ui.lock retirement. ok stop no longer SIGTERMs a lingering pre-retirement UI holder, ok clean no longer prunes a leftover ui.lock, and ok stop all / the uninstall flows no longer discover a lock directory that holds only a ui.lock. Builds before v0.60.0 wrote a ui.lock next to server.lock on every UI-serving start, so if one of those servers did not shut down cleanly the file may still be sitting in <project>/.ok/local/. It is inert — nothing reads it — but ok clean will no longer remove it: delete it with rm .ok/local/ui.lock. A server from one of those builds that is still running also still holds its server.lock, so ok ps and ok stop continue to see and stop it normally. What ok can no longer reach is a live UI process that is not the server itself: a standalone ok ui sidecar, an --only ui --server-url proxy, or the UI sibling that ok start auto-spawned before v0.53.0. In each of those, cat .ok/local/ui.lock reports the pid it recorded; stop it directly (kill <pid>, or taskkill /F /PID <pid> on Windows) before deleting the file.

  • Record what the server actually bound, and stop the spawn-error log destroying its own evidence.

    A server.lock advertises a port, but until now nothing recorded that a server had bound one — so a diagnostic bundle could not tell a live listener from a stale advertisement, or say which process owned a port when two servers had run for the same directory. The server now logs its pid, port, bound addresses and base URL at listen time.

    last-spawn-error.log was opened in truncate mode on every spawn, so a retry seconds after a failed spawn destroyed the output explaining the failure it was retrying. Both documented writers of that file — the desktop spawn and the MCP shim — now append behind a per-attempt header and start over only at a size cap, sharing one policy so neither erases what the other accumulated. The spawn-failure report bounds its stderr tail to the current attempt, so a child that dies silently no longer inherits the previous attempt's stack trace as its cause.

View v0.63.6 on GitHub