v0.12.0
Part of the OpenKnowledge changelog.
Minor Changes
Agent writes now get advisory mermaid feedback, delivered on a new unified
warningschannel. Thewrite/editMCP tools (and the underlyingPOST /api/agent-write-md/POST /api/agent-patchroutes) validate every```mermaidfence of the post-write document with the same mermaid version the editor renders with, and report parse failures aswarningsentries (kind: "mermaid-parse-error"— fence locator + mermaid's line-numbered message) plus a⚠line in the tool response text — so the authoring agent can fix a broken diagram in the same session instead of the reader discovering the error chrome. Strictly advisory: writes land byte-faithfully regardless.The
warningsarray is the one advisory channel going forward: it also carries the existing write-integrity kinds (content-divergence,disk-edit-reconciled) on every mutating write surface (write, write-md, patch, frontmatter-patch, rollback /restore_version), so co-occurring advisories no longer mask one another. The single-valuedwarningfield and the MCPstructuredContent.document.contentDivergencenesting are deprecated —warningkeeps emitting its highest-precedence integrity entry in parallel for one deprecation window; new consumers should readwarnings.The
palettetool's Mermaid entry now teaches the grammar sharp edges (raw;/#terminate sequence-family message text — use commas or#59;/#35;; quote flowchart labels containing punctuation), and the tool-description footer points at it.
Patch Changes
Soften the git auto-sync enable warning. The dialog shown before turning on auto-sync (onboarding and the settings/badge toggle) previously presented its caveats as an amber alert box with a bulleted "Before you enable it" list — alarming for what is a routine, reversible choice. It now reads as a calm "Heads up" section: three titled rows (Uncommitted changes, Commits happen automatically, Shared repositories), each with an icon and a short muted description. Same information, friendlier delivery; screen readers also no longer treat it as an interruption-priority alert.
Cascade new editor windows instead of stacking them dead-center. Electron centers every window that has no explicit position, so opening several projects — most visibly the post-update relaunch, which restores every previously open project at once — produced one indistinguishable pile of windows. Each new editor window now opens offset down-right from the focused (or most recently opened) window, macOS-document-app style, wrapping back to the top-left of the work area when it would run off the screen edge. The first window of a session still opens centered.
Open Knowledge Desktop again puts the
okcommand on your PATH automatically — politely. Launching the packaged app installs silent terminal shims (~/.ok/bin/okand~/.ok/bin/open-knowledgesymlinks into the app bundle, a~/.ok/env.shPATH shim, and a clearly marked managed block in your shell rc files —.zshrc, plus.bash_profileand fish config where present) with no admin prompt. Any user who has run Desktop at least once can typeokin a new terminal.Good-manners guarantees: the edit is disclosed in-app via a sticky toast that names the exact file(s) touched; the block is padded with blank lines and carries an inline opt-out hint; and deleting the block from an rc file is honored permanently — Open Knowledge records the opt-out and never re-adds it to that file. The
~/.ok/binshims themselves still self-heal on every startup, andOK_RECLAIM_DISABLE=1disables the whole mechanism.Unlike the earlier incarnation of this feature, Desktop no longer seeds
oksymlinks into other writable PATH directories (~/.cargo/bin,~/.local/bin, and the like) to make the command visible in already-open shells — that surprised users more than it helped. Startup now cleans up the symlinks earlier builds recorded, removing each one only while it still points at the recorded bundle target; anything re-pointed or foreign is left alone. The/usr/local/binadmin-prompt installer remains retired.Extinguish the non-Playwright flake classes that were dropping merge-queue runs and breaking local tiers. Integration tests can now await the shadow-repo WIP commit deterministically via a new dev-only
POST /api/test-flush-gitroute instead of racing the fire-and-forget git pipeline against a 20-second budget (the rename-history class behind three queue drops in six days). Parse-health metrics split wall-clock parse-budget aborts (parseFallback.wholeDocBudget, an environmental signal) from structural whole-doc fallbacks (parseFallback.wholeDoc, a content-health signal), so fixed-corpus "zero whole-doc" assertions and operator alerts no longer trip when a loaded machine crosses the 500ms defense budget on content that parses fine when idle. The two long-broken a11y PropPanel tests hover-reveal the chrome before clicking the gear. Full audit:reports/nonplaywright-flake-audit-2026-06/.Fixed: a trailing space or tab typed at the end of a frontmatter fence line in source mode no longer destroys the document's frontmatter on the next visual edit. Frontmatter fence recognition now tolerates trailing spaces/tabs on the
---lines (matching CommonMark / remark-frontmatter behavior), so documents whose fences carry trailing whitespace are recognized consistently across the editor bridge, the property panel, templates, search heading extraction, outline navigation, skill bundle metadata, the CLI, and source-mode syntax styling. Leading whitespace before a fence still disqualifies it, as before.For maintainers — mechanism:
FRONTMATTER_RErejected fence lines with trailing whitespace while the bridge tolerance layer (normalizeBridgestep 7b, the per-line trailing-whitespace strip) declared exactly those bytes equivalent, violating partition invariance: an in-tolerance W2 keystroke (---→---) silently changed FM-region recognition, so Observer A Path B's recompose (prependFrontmatter(readCurrentFm(), serialize(fragment))) fabricated an FM deletion that the three-way merge then applied and settled. The recognition predicate is widened to/^---[ \t]*\r?\n([\s\S]*?\r?\n)?---[ \t]*(\r?\n|$)/at its definition boundary in core, with every sibling recognition site (server template/heading/enrichment/page-identity/templates-listing/skill-metadata parsers, CLI parser, CodeMirror decoration scoping and outline navigation) either rewired through the core functions or sharing the line-scoped form of the same contract.Skip the "Updated to Version ..." release-notes notice on a fresh install. A brand-new install has no prior version, so everything is new — popping release notes at first launch was noise. The first launch now silently records the installed version as the baseline; the notice fires only on a real version transition (the next update).
Freeze table header row and first column when scrolling
Long tables now keep the top row (column headers) visible at the top of the editor as you scroll past it, and the first column always stays visible during horizontal scrolling.
Hide the "Edit with AI" button in the WYSIWYG bubble menu when Open Knowledge is embedded inside an agent host (Cursor, Codex, Claude Desktop). The button opens the header's "Open with AI" menu, but that menu is already hidden in embedded hosts — so clicking the bubble button there did nothing. The button (and its Cmd+Shift+I shortcut) now hide in lockstep with the header menu, matching the rest of the embedded-host chrome.
Hide the sidebar "New from template" entry when a folder or the project has no templates. Previously, right-clicking a folder always showed a "New from template" submenu that opened to an empty list, and the project empty-space menu showed the entry greyed-out rather than hidden. Both surfaces now drop the entry entirely when there are no templates to pick, matching the toolbar and the editor empty-state.
Fixed: the first WYSIWYG edit to a freshly loaded document no longer misroutes through a concurrent-edit merge that falsely reported divergence, and byte-unsafe constructs the canonical serializer would rewrite (un-padded GFM tables, inline-math delimiter form, PUA sentinels, multi-blank-line runs) are now preserved across WYSIWYG edits and editor mount — visually editing such a document no longer rewrites untouched constructs, and a mere editor mount no longer mutates the file on disk. Whitespace-only differences within bridge tolerance on untouched lines (e.g. the blank line between the frontmatter closer and the body that most real-world docs carry) are normalized on that first WYSIWYG edit — the documented transition behavior of the Y.Text-is-truth contract's architectural floor.
For maintainers — mechanism: the sync baseline previously recorded the canonical serialization where the router strict-compares raw Y.Text bytes, so the first WYSIWYG edit after loading a non-round-trip-byte-stable doc (and after any source-mode edit Observer B absorbed) misrouted through Path B's three-way merge with telemetry falsely asserting
ytextDiverged: true. The baseline is now split into two witnesses — canonical serialization for the fragment-unchanged short-circuit; raw Y.Text bytes for the router and the divergence-merge base — and the in-sync leg splits on residual tolerance: within tolerance routes Path A's canonical rewrite; beyond tolerance applies only the fragment's canonical-space delta onto the raw bytes via a canonical-base three-way merge. Source-mode edits within bridge tolerance (e.g. trailing whitespace) are still real divergence and still merge byte-preservingly through Path B.For operators:
observer-a-path-b-firedand theobserverAPathBFires/observerAPathBFiresSuppressedcounters are now scoped strictly to real unabsorbed Y.Text divergence, and the event'sytextDivergedattribute is derived from the routing decision instead of hardcoded — dashboards keyed on the event will see the rate drop on residual-bearing docs whose first-edit fires were previously misclassified. The in-sync canonical-base residual merge is not a divergence fire; its volume is visible via the newobserverAResidualMergeRunscounter and theobserver.a.pathspan attribute (path-a|residual-merge|path-b).Fix silent loss of unchanged content after a document editor opens. The editor's pre-warm fast path built its node cache against a throwaway internal schema instance; because the editor matches content by schema-instance identity, the first incremental collaborative or agent edit after opening could silently drop unchanged sibling paragraphs from the visible editor, and the next click or keystroke could republish that truncated copy over the shared document — erasing the content for every peer and on disk. The pre-warm walk now runs during editor construction against the editor's own schema, so cached nodes always match and unchanged content survives incremental updates.
preview_urlnow treats a preview request as demand for a backend. When no Open Knowledge server is running for the project, the tool auto-starts one through the same machinery the read/write tools use (OK_MCP_AUTOSTARTgate, spawn timeout, spawn-error log) and waits briefly for the preview UI sibling to bind, so an agent whose first action is "open the preview" gets a working URL instead ofrunning: false. The ensure also runs when a UI is already up, which revives the backend for a surviving preview UI whose server idle-shut-down. When no UI can be reached the recovery hint now names the right command for the actual state:ok startwhen nothing is running (previously the hint saidok ui, which produces a backend-less UI shell in that state) andok uiwhen the server is alive but no UI is bound. Auto-start opt-out (OK_MCP_AUTOSTART=0) keeps today's soft not-running answer; spawn failures surface as tool errors with the spawn log attached.Surface update-relaunch failures that happen after the relaunch was committed. Squirrel.Mac reports install failures asynchronously (through the updater's error event, or by silently never quitting) — previously every window stayed stuck on the terminal "Relaunching to install the update…" card with no button and no dismiss while the app kept running. Now the main process treats an updater error while a relaunch is in flight as that relaunch failing, backed by a watchdog that fires if the app is still alive 15 seconds after a clean quitAndInstall return. Either trigger restores the staged update, swaps every window back to the actionable "Version X ready to install" banner, and shows a "Relaunch failed — please restart manually" notice with the failure detail. A false watchdog alarm self-heals: if the app does relaunch, the restored state is cleared on the next boot.
Fix the update "Relaunch" notice so it propagates to every open window. Previously, clicking "Relaunch" on the "Version X ready to install" banner swapped only that one window to "Relaunching to install the update…"; any other open windows kept showing the stale, still-clickable "ready to install" banner for the several seconds it takes to tear down each project's server before the app quits and reinstalls. Now the relaunch fans out to all windows the instant it commits in the main process, so every window swaps to the in-progress "Relaunching…" card in lockstep and the Relaunch button can't be fired a second time from another window.
Sync now authenticates to GitHub using your
ghCLI login, matching how clone already works. Previously the background sync engine only consulted Open Knowledge's own credential store, so users who were signed in withghbut had never completed a separate Open Knowledge sign-in saw a persistent "GitHub sign-in is missing or expired" error in the sync popover even though theirghsession was valid. The server now resolves theghtoken (the same source the push-permission check already used) and relays it to the git credential helper, which prefers it over its own stored token. A freshgh auth loginor a reconnect is picked up on the next sync cycle without a restart.Also fixes
ok cloneso itsgh-based authentication works on installs whereghlives outside the default PATH (e.g. Homebrew at/opt/homebrew/bin): clone now inherits your shell environment instead of replacing it, so theghcredential helper can locateghand its config.Fixed: placing the cursor in a table cell no longer pushes the whole document down. The invisible positioning layer for the table's column/row handle pills (
ok-table-cell-handle-layer) claimed its own row in the editor's layout grid, and on documents shorter than the window that empty row was stretched to an equal share of the leftover height — a blank band appeared above the document whenever a table was focused. The layer is now pinned into the content's own grid row at zero height, so the handle pills render in exactly the same place with no layout shift.For maintainers — mechanism:
.tiptap-editoris a CSS grid with implicit auto rows, and the defaultalign-content: stretchdistributes leftover block-size equally across in-flow auto tracks; any zero-height direct child still generates a stretchable track. The layer now carriesgrid-row: 1; align-self: start, and.tiptap-editor-portal-contentdeclaresgrid-row: 1explicitly so auto-placement doesn't bump the editor body to row 2.Color the "Updated to Version X" sidebar notice green. The post-update confirmation previously used the same gray styling as the "Version X ready to install" notice, making the two easy to confuse at a glance. The confirmation card now renders with a green border, tint, and text so a completed update reads differently from one that is still waiting on a relaunch.
MCP project routing now sticks to the worktree you name. Pass
cwdonce on an OK tool call and latercwd-less calls in the same session reuse it, so an agent working in a git worktree no longer has its reads, writes, and preview silently target the main checkout. When acwd-less call resolves into a repo that has multiple git worktrees via the client's single advertised root, the server emits a one-time, non-blocking nudge to passcwd— it does not refuse, so working in the main checkout while feature worktrees exist is unaffected.