github saberzero1/motions 1.0.0

one day ago

Added

  • Neovim RPC connection lifecycle — adds an opt-in desktop-only backend foundation that spawns a user-configured Neovim, attaches with an in-repo msgpack-RPC client, requires API level 12 (Neovim 0.12+), reports actionable startup/crash errors, and terminates the child on setting disable, Vim disable, plugin unload, or failed attach. Milestone 1 intentionally does not forward keys, synchronize text, or bridge decorations.
    • Plugin: src/rpc/msgpack-rpc.ts, src/rpc/neovim-connection.ts, src/main.ts, src/settings.ts
  • Neovim RPC active-editor text synchronisation — mirrors the active Markdown editor into the single Neovim buffer on connection and leaf activation, then applies content-carrying nvim_buf_lines_event notifications to CM6 with byte-to-UTF-16 coordinate conversion. Neovim is authoritative for RPC-originated text; key delegation, multi-leaf buffers, decorations, IME, and frontmatter policy remain deferred.
    • Plugin: src/rpc/document-sync.ts, src/rpc/msgpack-rpc.ts, src/rpc/neovim-connection.ts, src/main.ts
  • Dedicated Neovim configuration settingneovimConfigPath optionally points the backend at a minimal Obsidian-specific init.lua, avoiding terminal-only LSP, dashboard, and statusline startup. Empty preserves the production default of loading the user's normal configuration. A configured path starts under --clean, prepends its directory to runtimepath, and loads only that file with -u; measured startup dropped from 115 ms to 24 ms locally.
    • Plugin: src/settings.ts, src/main.ts, src/rpc/neovim-connection.ts
  • Neovim RPC key delegation — while connected, a lifecycle-owned capture handler forwards every non-composition editor key with nvim_input, suppresses the bundled fork through setKeyInterceptActive, prevents CM6 input, and synchronizes cursor and exposed mode state after a blocking RPC barrier. Every active-leaf activation seeds Neovim from that editor. IME composition remains deferred to M6.
    • Plugin: src/rpc/key-delegation.ts, src/rpc/document-sync.ts, src/rpc/neovim-connection.ts, src/main.ts
  • Neovim RPC decorations — a bundled Lua companion registers one redraw-driven decoration provider, queries all namespaces over visible buffer ranges, and forwards highlight and virtual-text extmarks in buffer coordinates. The host attaches a fixed-size UI only as a redraw clock, discards grid events, maps byte columns through the text-sync coordinate path, resolves Neovim highlight groups, and clears CM6 decorations on disconnect. The companion remains a real .lua source file embedded into main.js by esbuild's text loader; no runtime file or user configuration is modified.
    • Plugin: src/rpc/companion.lua, src/rpc/decorations.ts, src/rpc/document-sync.ts, src/rpc/msgpack-rpc.ts, src/rpc/neovim-connection.ts, src/types/lua-modules.d.ts, src/main.ts, esbuild.config.mjs
  • Neovim RPC write and read routing — the named mirror is now buffer-locally buftype=acwrite. A buffer-scoped BufWriteCmd routes :w and :w! through Obsidian's editor:save-file command and clears Neovim's dirty flag; a buffer-scoped BufReadCmd makes :e and :e! re-seed from the current Obsidian document with line-event echo suppressed instead of loading stale disk content.
    • Plugin: src/rpc/companion.lua, src/rpc/decorations.ts, src/rpc/document-sync.ts
  • Neovim RPC Obsidian feature bridge (M4a) — generates selected mappings and user commands from VimRegistration, dispatches every host action through one obsidian_action notification channel, and restores the files picker, Oil, Harpoon slot 1, vertical split, next-heading navigation, and lowercase :sidebar while Neovim owns editor keys. Lowercase commands register an uppercase Neovim command plus a start-of-command-line guarded abbreviation. Refresh and disconnect remove mappings, commands, abbreviations, and notification listeners before reinstalling.
    • Plugin: src/rpc/obsidian-feature-bridge.ts, src/rpc/neovim-connection.ts, src/rpc/key-delegation.ts, src/vim/registration.ts, src/picker/picker.ts, src/main.ts
  • Neovim RPC workspace and navigation bridge (M4b Batch 1) — extends the registry-driven bridge with pane focus/cycling, horizontal splitting, tab close/cycle/target actions, counted Ngt, previous-pane and alternate-file state, pane-to-tab moves, four go-to-definition variants, and 11 lowercase workspace ex callbacks. A general dispatch payload carries mapping counts and command arguments for later parameterized batches; guarded abbreviations keep command names inert inside substitutions.
    • Plugin: src/rpc/obsidian-feature-bridge.ts, src/vim/registration.ts, src/keybindings/action-registry.ts
  • Neovim RPC picker bridge (M4b Batch 2) — generates the remaining built-in picker leader mappings and picker ex callbacks in Neovim. The existing general command payload preserves grep queries and named picker sources, source-specific marks/register pickers and resume reuse the ordinary picker entry point, modal keys remain owned by Obsidian, and file selection re-seeds Neovim through active-leaf activation.
    • Plugin: src/rpc/obsidian-feature-bridge.ts
  • Neovim RPC Harpoon, marks, and jumplist bridge (M4b Batch 3) — generates every remaining Harpoon mapping and ex callback, :marks/:delmarks/:jumps, and host-owned <C-o>/<C-i>. Slot strings and mapping counts use the existing payloads; cross-note navigation waits for active-note re-seeding and restores the stored cursor in both CM6 and Neovim. Lowercase within-buffer marks remain Neovim-native, while uppercase cross-file mark motions are explicitly deferred.
    • Plugin: src/rpc/obsidian-feature-bridge.ts, src/rpc/neovim-connection.ts, src/rpc/document-sync.ts, src/vim/harpoon-nav.ts, src/workspace/global-defaults.ts, src/main.ts
  • Neovim RPC folding and undo-tree integration (M4b Batch 5) — keeps all fold and undo operations native to Neovim, forwards visible fold state beside extmarks on the existing redraw pass, mirrors matching CM6 folds, supplies a Markdown-aware window-local foldexpr, and bridges only the three Obsidian sidebar lifecycle commands. The sidebar renders Neovim's native undotree() result; 64-bit msgpack integers are decoded for its timestamps. Fold persistence is intentionally unavailable under RPC rather than restoring host offsets into Neovim-owned state.
    • Plugin: src/rpc/companion.lua, src/rpc/decorations.ts, src/rpc/frontmatter-fold.ts, src/rpc/key-delegation.ts, src/rpc/msgpack-rpc.ts, src/rpc/obsidian-feature-bridge.ts, src/vim/undo-tree.ts, src/vim/undo-tree-view.ts, src/main.ts
  • Neovim RPC structural navigation and hard-wrap (M5a) — moves heading, level-specific heading, same-indent list, and Markdown-link motions out of the host feature bridge and into buffer-local companion mappings backed by Neovim's bundled Markdown treesitter parsers. Counts and operator-pending ranges match the bundled fork. The mirror receives the configured textwidth, while native gq/gw and the stock Markdown ftplugin own wrapping.
    • Plugin: src/rpc/companion.lua, src/rpc/document-sync.ts, src/rpc/decorations.ts, src/rpc/neovim-connection.ts, src/rpc/obsidian-feature-bridge.ts, src/motions/register.ts, src/main.ts
  • Neovim RPC Markdown text objects (M5b) — installs buffer-local operator-pending and visual mappings for emphasis, inline code, math, strikethrough, Markdown links and wikilinks, fenced code blocks, nested blockquotes, callouts, HTML tags, table cells, and table rows. Native Markdown treesitter supplies structural ranges; native it/at supplies tag matching with fork-compatible count handling. Explicit visual ranges keep every operator bounded, and teardown removes every mapping.
    • Plugin: src/rpc/companion.lua
  • Neovim RPC floating-window bridge (M6a) — enumerates floats during the existing redraw provider's on_end, forwards per-window config, buffer content, and extmarks without grid events or polling, and renders positioned Obsidian overlays with border presence and Neovim z-index. CM6 character/line metrics map terminal cells approximately onto proportional Markdown typography; cursor- and window-relative origins are resolved separately. Closed windows and disconnected sessions remove their overlays.
    • Plugin: src/rpc/companion.lua, src/rpc/decorations.ts, src/rpc/floating-windows.ts
    • Styles: styles.css
  • Neovim RPC IME composition bridge (M6b) — a cursor-positioned input target outside CM6's contentDOM owns native composition and keeps preedit out of Neovim. Commits enter through nvim_input, preserving insert undo and dot-repeat; ordinary keys are suppressed during composition, while Escape, blur, mode changes, active-note changes, and disconnect cancel preedit and resynchronize ownership.
    • Plugin: src/rpc/ime-input.ts, src/rpc/key-delegation.ts
    • Styles: styles.css
  • Neovim RPC external-UI messages (M8a) — attaches the messages, command-line, and popup-menu UI extensions once, dispatches ordered redraw batches while rejecting unhandled grid events before further work, and routes D12 error, warning, and informational message kinds to severity-styled Obsidian Notices with a five-second duplicate cooldown. Routine undo, search, progress, completion, and command-list kinds remain silent; command-line and popup-menu rendering remain deferred.
    • Plugin: src/rpc/redraw.ts, src/rpc/messages.ts, src/rpc/decorations.ts, src/rpc/neovim-connection.ts
  • Neovim RPC external command line (M8b) — renders cmdline_show, byte-correct cmdline_pos, cmdline_special_char, and level-scoped cmdline_hide events in a themed editor overlay. First characters, prompts, confirm choices, and nested levels make typed commands, searches, vim.ui.input(), and generic vim.ui.select() visible without changing bundled-fork behavior; popup-menu completion remains deferred.
    • Plugin: src/rpc/cmdline.ts, src/rpc/neovim-connection.ts
    • Styles: styles.css
  • Neovim RPC popup-menu completion (M8c) — renders popupmenu_show, selection updates, and hide events as a themed four-column completion list. Command-line wildmenu uses byte-correct command-line anchoring, while insert completion uses Neovim grid cells and measured CM6 editor metrics.
    • Plugin: src/rpc/popupmenu.ts, src/rpc/cmdline.ts, src/rpc/neovim-connection.ts
    • Styles: styles.css
  • Neovim RPC status-bar mode ownership (M8d) — routes msg_showmode into the existing status bar, gives the externally supplied Neovim mode precedence over fork events while RPC is connected, and restores fork-driven mode text on disconnect.
    • Plugin: src/rpc/mode-status.ts, src/rpc/neovim-connection.ts, src/vim/mode-tracker.ts, src/main.ts
  • The table-nav overlay was never broken under the Neovim backend, and the documentation saying otherwise is corrected — no code changed. canActivate() gates on forkAvailable, which is !isBuiltinVimEnabled(app) — that Obsidian's own vim is off, not that the bundled engine is driving keys — so the overlay already activated under RPC, and reading that as "requires the bundled engine" was the error. Measured under a live connection rather than argued: l moved the highlighted cell from column 0 to column 1 while Neovim's cursor row stayed at 3 and the buffer was byte-identical, because Obsidian's keymap scope consumes the key before the delegation listener on the editor sees it. The risk worth ruling out was the opposite one, a key driving both the overlay and Neovim, and it does not occur.
    • Docs: docs/features/neovim-backend.md, KNOWN_LIMITATIONS.md
  • Companion mappings and leader groups describe themselves to which-key plugins — the readable-description work covered the feature bridge but not the companion, which registers 18 structural motions, 26 Markdown text objects and the fold aliases as real Neovim mappings; all of them still carried ids like vim-motions-rpc-textobject:iC. They now read as prose, and checking the object table while writing the labels caught two I would have guessed wrong: iC is the code fence and io the callout, not the other way round. Leader prefixes are also registered as <Nop> mappings carrying only a group label, so which-key.nvim renders +find and +harpoon menus rather than a flat list of leaves. Nothing executes those stubs — Neovim resolves the longer binding — and the leader itself is never mapped, since mapping it would make it a complete binding and break every leader sequence.
    • Plugin: src/rpc/companion.lua, src/rpc/obsidian-feature-bridge.ts, src/rpc/neovim-connection.ts, src/main.ts
  • Hint mode works with the Neovim backend, and bridged bindings describe themselves — hint mode needed far less than expected: its overlay captures keys on the document in capture phase, ahead of the delegation listener on the editor, and stops propagation, so label keystrokes could never have reached Neovim. Only the trigger was missing, and the five hint* ex callbacks plus the hintMode leader mapping are ordinary registrations the bridge already installs. Flash and EasyMotion share that same captureKeys path, so their triggers are one allowlist entry away too; they are deliberately left out because s and <leader><leader> already mean something in Neovim. Separately, every generated mapping and command now carries a readable desc prefixed Vim Motions: rather than vim-motions-rpc:<id>, so :map, :command and which-key.nvim describe them in words. Which-key itself is not ported: it renders the plugin's leader registry from the fork's key stream, which is not the keymap in force under RPC, whereas which-key.nvim draws in a floating window the bridge already renders.
    • Plugin: src/rpc/obsidian-feature-bridge.ts
  • Cursor shapes and input-method switching follow Neovim's mode — both read the bundled fork's own state or its vim-mode-change event, which never moves while Neovim owns keys, so the per-mode cursor shape stayed on normal throughout an RPC session and automatic IM switching never fired. src/vim/external-mode.ts is one seam the connected backend publishes Neovim's mode into, mapped to the plugin's vocabulary; the animated cursor consults it ahead of the fork through resolveVimModeWithExternal, and the input-method watcher subscribes alongside the adapter event. It publishes at connect as well as on every key — a gap the new lifecycle scenario caught, since onMode only fires after a keystroke and the mode was null until the user typed — and clears on disconnect so the fork regains ownership. Which-key and hint mode stay on the fork's event stream: they need key events rather than a mode.
    • Plugin: src/vim/external-mode.ts, src/vim/animated-cursor/controller.ts, src/im/im-mode-watcher.ts, src/rpc/neovim-connection.ts, src/main.ts
  • JSON snippets work with the Neovim backend through LuaSnip — the plugin's snippet JSON is plain VS Code format with the standard variable set and nothing Obsidian-specific, so LuaSnip reads it verbatim; verified before building by expanding date from the bundled global.json through a real LuaSnip, which produced the resolved date rather than the literal variables. Set up Neovim now installs LuaSnip, writes the bundled snippets out of main.js to vim-motions-snippets/ beside the generated config, points the loader at those and at the user's own snippet directory as an absolute path, and maps <Tab>/<S-Tab> to expand and jump — without an expansion key the block would load and do nothing. The Lua DSL remains incompatible: different namespace and registration signature, and its reactive f()/d() nodes call into Obsidian's vault.
    • Plugin: src/rpc/config-export.ts, src/main.ts
  • The generated dial.nvim configuration restores the behaviours that made the feature worth having — its default augends are decimal, hex numbers, four date patterns and Japanese weekdays, so wiring only the keymaps would have lost booleans, hex colours and checkboxes, which are most of what the bundled increment adds over Vim's own. Those three augends are now registered explicitly. Verified against the real plugin rather than asserted: flag = truefalse, #aabbcc#abbbcc, - [ ] task- [x] task, 4142. This also corrects a documentation claim that dates were unsupported by default; they are.
    • Plugin: src/rpc/config-export.ts
  • Set up Neovim installs and updates the plugins it configures, on request — the configuration export previously refused to install anything, on a reading of the Developer Policies that was stricter than the policy is: the "install or update their dependencies" clause targets a plugin pulling in what it needs unasked, not an install the user explicitly requested, which is why BRAT ships in the catalogue. Set up Neovim now previews the plugins Neovim will fetch or update and the configuration that will be written, and applies both only on confirmation, using Neovim's own vim.pack into Neovim's data directory. Installing is deliberately kept on the button rather than written into the generated file, so a file you require stays configuration. Measured before building: vim.pack.add and vim.pack.update are both non-interactive headless, the latter needing force = true to skip its review buffer.
    • Plugin: src/rpc/config-export.ts, src/rpc/neovim-connection.ts, src/settings.ts, src/main.ts, styles.css
  • --clean silently disabled Neovim package activation — with a configured Neovim configuration path the backend spawns under --clean, which strips the user packpath. vim.pack then clones a plugin to disk and never puts it on the runtimepath, so require still fails: an install that reports success and does nothing. Measured directly — rtp_has_plugin=false under --clean against true without it. The spawn now appends Neovim's own stdpath('data')/site back, which restores packages without reinstating the wrapper-injected runtimepath --clean exists to exclude; a configured init.lua still loads.
    • Plugin: src/rpc/neovim-connection.ts
  • Generate a Neovim configuration from your settings — most bundled-engine features were modelled on a Neovim plugin, so Vim engine → Generate a Neovim configuration translates the enabled ones into nvim-surround, dial.nvim, spider.nvim, yanky.nvim, flash.nvim, and mini.operators, alongside the leader key and textwidth. It writes lua/vim_motions.lua beside the user's Neovim configuration and does nothing until they add require('vim_motions') themselves; a Copy button serves the pick-and-choose case. The path must be under lua/ to be requireable — a file beside init.lua is not, measured — and it is resolved by asking Neovim for stdpath('config') rather than reconstructing it from XDG_CONFIG_HOME and NVIM_APPNAME, which M2a measured as unreliable against wrapper installs. Every block is wrapped in pcall(require, …) so the file loads with none of the plugins present, and it emits no install call: this configures what the user already has, never fetches it. Regenerating refuses to overwrite a file whose generated header is gone. A changed setting marks the file stale in the settings tab, and Regenerate automatically (default off) applies it without asking. Both settings implementations render this through one shared method rather than two copies.
    • Plugin: src/rpc/config-export.ts, src/util/external-fs.ts, src/settings.ts, src/main.ts, styles.css
  • Smart list continuation and yank highlight now work with the Neovim backend — both are driven by the existing plugin settings and need no Neovim plugin. o/O continues -, *, and + bullets, which the stock Markdown ftplugin does not do (formatoptions=jtcqln omits o and r). It cannot simply drop the ftplugin's f comment flag: that flag serves two behaviours at once — it gives gq its hanging indent while stopping o repeating the marker — and removing it made gq re-bullet every wrapped line, which rpc-structural-nav caught. The continuation form is swapped in only for the duration of an o/O insert, through an expr mapping returning the key so count, undo, and dot-repeat stay native; numbered lists remain out of reach because comments cannot increment a counter. Yank highlight is reported by a TextYankPost notification and drawn by the host's own renderer, so fade works as well as solid. The obvious extmark route does not: a yank changes no text, so the decoration provider never re-runs and the mark reaches CM6 only on a later redraw, long after the highlight expired. textwidth, previously a lone positional argument, now travels with both as one editor-options object.
    • Plugin: src/rpc/document-sync.ts, src/rpc/neovim-connection.ts, src/main.ts
  • Arbitrary Obsidian commands are reachable from Neovim:ob and :obcommand join the feature bridge, so :ob theme:use-dark executes any Obsidian command by ID while RPC owns editor keys. The bridge is otherwise an allowlist of features this plugin registers, which by construction cannot enumerate commands owned by Obsidian itself or by other plugins; this is the one endpoint that escapes it. Both route through the existing { id, args } payload and the start-of-command-line guarded abbreviation, so :%s/ob/…/ stays a substitution.
    • Plugin: src/rpc/obsidian-feature-bridge.ts
  • Neovim backend crash breadcrumb — a persisted neovimToggleInFlight marker is written before a real RPC connect or disconnect and cleared once it settles, so a marker that survives a restart means the renderer died mid-switch. The next start clears it and explains what happened instead of leaving an unexplained lost window. The renderer crash itself cannot be caught in-process: the plugin's JavaScript dies with the renderer, and Electron's UtilityProcess is unavailable to plugins. Scoped to real transitions — a reload that neither connects nor disconnects writes nothing.
    • Plugin: src/main.ts, src/settings.ts
  • treesitter-handle-leak ast-grep rule — flags a TreeCursor allocated with .walk() whose enclosing scope never calls delete() on it. web-tree-sitter registers every handle with a FinalizationRegistry and delete() unregisters it first, so a dropped cursor is not a leak — it is a deferred free of its tree's pointer (the registry holds this.tree[0], not the cursor's own handle) from a GC callback, after the CM6 bridge has already freed that tree. Shown to fire on the real defect, getAllNodesOfType at 88ec375~1, and to stay silent on the fixed tree, per the repo rule that a new pattern rule must be demonstrated against a defect that actually shipped.
    • Coverage was measured against a probe of every form rather than assumed: it fires on const/let/var bindings never freed in scope, on a discarded node.walk();, and on a reassignment in a scope with no delete() at all, with no false positives across four correctly-freed shapes. The discarded-value arm uses a direct-child has deliberately — with stopBy: end it also matched cursor = node.walk(); and flagged a correctly freed reassignment. It is flow-insensitive, so a reassignment in a scope that frees some other cursor is a known false negative, accepted because the alternative flagged correct code. Tree and Query handles are out of scope, since parse() and new QueryWrapper() legitimately transfer ownership to a cache or caller; their owners are asserted by unit tests instead.

Changed

  • Minimum Obsidian version raised from 1.7.2 to 1.8.7 — RPC message severity styling applies its class to Notice.messageEl, which noticeEl predates; messageEl is available from 1.8.7. Vaults on 1.7.2 through 1.8.6 will not receive this release. README.md and the installation guide state the new floor.
  • The bundled vim fork's own type declarations are usedsrc/types/codemirror-vim.d.ts declared Vim as Record<string, unknown>, shadowing the 166 members the fork's shipped .d.ts exports and reducing every Vim.* access across the 19 files that import it to unknown. All 15 fork-specific exports it hand-declared (foldopenAnnotation, setCursorSuppressed, setKeyInterceptActive, …) were already shipped, so deleting it reports zero errors in src/ and test/ while restoring real checking. The casts that erasure forced are gone: escape-guard.ts now calls Vim.setIdleEscapeCallback directly, and the demand harness calls Vim.map/unmap/getOperatorfunc without a hand-written view. getBundledVimApi() keeps its conversion — the plugin's own VimApi interface genuinely does not overlap the fork's shape.
    • Plugin: src/types/codemirror-vim.d.ts (removed), src/vim/escape-guard.ts
  • RPC E2E provisioning is cross-platform and version-pinned — Linux uses the official Neovim 0.12.5 release tarball in the E2E container, while macOS and Windows install the matching official release in the workflow. Every installer prints nvim --version and rejects API levels below 12 before tests start.
    • CI: .github/docker/e2e-runner/Dockerfile, .github/workflows/docker-e2e-runner.yml, .github/workflows/e2e.yml, .dockerignore
    • Scripts: scripts/install-neovim.sh, scripts/install-neovim.ps1, scripts/neovim-version.txt
  • Neovim RPC frontmatter handling supports both properties modes — removes the Source-only connection refusal. Source frontmatter remains fully navigable; rendered frontmatter receives a dedicated-window foldmethod=expr fold using the same start-of-document delimiter rule as CodeMirror, and post-key cursor synchronization resolves foldclosed() positions to the first body line. Property-widget focus remains owned by Obsidian, while API edits can still update the complete folded document.
    • Plugin: src/fold/frontmatter.ts, src/fold/provider.ts, src/rpc/frontmatter-fold.ts, src/rpc/document-sync.ts, src/rpc/key-delegation.ts, src/rpc/neovim-connection.ts
  • The renderer no longer parses Markdown while the Neovim backend is connected — Neovim parses the same document natively, and the renderer consumers of its tree are dormant then: structural motions and Markdown text objects run as companion mappings inside Neovim, and fold state is mirrored back from redraw. The tree-sitter bridge now tracks the connection, so a connected session parses each change once instead of twice. It also removes the WASM heap growth that made retained tree-sitter nodes reachable — defence in depth, since that defect is fixed at the call sites. Verified both ways: 34 RPC tests pass with the bridge off, 33 non-RPC tests pass with it on.
    • Plugin: src/main.ts

Fixed

  • ]h and [h waited a second before moving — M5a added ]h1]h6 and [h1[h6 as descriptive aliases for the level-specific heading motions, which the plugin already binds to ]1]6 and [1[6. The aliases made ]h a prefix of six longer mappings, so every press hit the fork's prefix-ambiguity deferral and waited out operatorshadowtimeout (1000 ms) before moving. The redundant aliases are gone; the numeric bindings are unchanged.
    • Plugin: src/motions/register.ts, src/rpc/companion.lua
  • A Neovim RPC request could stay pending forever — a request whose response never arrived left its promise unsettled with no bound, so a wedged Neovim silently hung whatever awaited it. Requests now reject after 30 seconds. The timer is taken from window, not the Node global, so it also fires correctly in Obsidian popout windows.
    • Plugin: src/rpc/msgpack-rpc.ts
  • A rapid Vim-mode toggle applied only its first halfdisableVim and enableVim cleared their in-progress flag from a 500 ms timer armed in finally, so the returned promise resolved while the flag was still set. The opposite toggle arriving inside that window hit its own guard and was discarded with nothing to retry it, leaving Vim off and every extension-slot feature unregistered until Obsidian reloaded. Toggles are now serialized so both halves apply.
    • Plugin: src/main.ts
  • Heading motions became no-ops before the parser settled]h/[h and the level-specific motions consulted the treesitter tree and gave up when it existed but had produced no headings yet, which happens while parsing is still catching up. They now fall back to the regex scanner in that case, as they already did when no tree was available at all.
    • Plugin: src/motions/headings.ts
  • Two treesitter nodes were held across traversals that can free them — snippet context detection read a fenced block's language from a node retained across a child walk, and the blockquote text object held a node across its .parent() ancestor walk. Both are the use-after-free shape behind the renderer crash class: the handle survives the tree that owns it and then returns stale data. Both now read through a cursor while traversing.
    • Plugin: src/snippets/context.ts, src/text-objects/blockquote.ts, src/treesitter/js-api.ts
  • RPC fold expression no longer rescans the complete document for every queried line — the window-local Markdown foldexpr previously fetched and rescanned all lines on each invocation, making an edit on a 2,004-line note effectively quadratic and pushing measured RPC operator latency to 145.9 ms p95. It now computes one linear fold-level table per Neovim changedtick and reuses it for the remaining fold queries.
    • Plugin: src/rpc/frontmatter-fold.ts
  • RPC requests wait for active-note re-seeding — programmatic Neovim requests issued immediately after returning from a host sidebar could race the asynchronous active-leaf activation and have their buffer update replaced by the later seed. Requests now await the document-sync activation promise before flushing keys or reaching Neovim.
    • Plugin: src/rpc/neovim-connection.ts
  • RPC notes now use a real Markdown buffer instead of the dashboard buffer — M2a previously wrote into Neovim's intro buffer by forcing its modifiable option. With alpha-nvim loaded that buffer remained buftype=nofile and filetype=alpha, preventing Markdown plugins and filetype configuration from activating even though byte synchronisation passed. The backend creates one listed buffer, names it with the active note's absolute path, makes it current, explicitly runs filetype detection, and reuses it across active-leaf changes. The buffer is now finalized as the acwrite mirror described above.
    • Plugin: src/rpc/document-sync.ts, src/rpc/msgpack-rpc.ts
  • RPC acceptance tests no longer load the developer's Neovim configuration — both lifecycle and text-sync specs use the committed minimal fixture. A Lua marker assertion fails if the configured path is ignored or cleared.
    • Tests: test/fixtures/nvim/init.lua, test/specs/rpc-lifecycle.e2e.ts, test/specs/rpc-text-sync.e2e.ts
  • RPC leaf changes no longer overwrite the newly active note with the previous note — the M2b one-shot seeding path reused the previous Neovim mirror on every later activation and dispatched it into the new CM6 editor, allowing Obsidian autosave to persist cross-note data loss. Activation now always reads the newly active editor, renames the Neovim buffer, detects its filetype, and repopulates it while line-event echo is suppressed. No activation path writes an existing mirror into a different editor.
    • Plugin: src/rpc/document-sync.ts
  • Harpoon same-leaf navigation preserves stored cursor positions — changing the file in one leaf fired active-leaf-change after that leaf already represented the destination, so cursor tracking overwrote the destination pin with line 0, column 0. Same-leaf activations no longer treat the destination as the leaf being left, and navigation snapshots its target before asynchronous activation.
    • Plugin: src/main.ts, src/vim/harpoon-nav.ts
  • Oil sort cycling changes the rendered order — directory rendering always read the configured default sort value, so gs advanced an internal key that was never used. The manager now initializes its active sort from the configured default and renders with the cycled value.
    • Plugin: src/oil/manager.ts
  • Oil hidden-file toggle recognizes an unchanged buffer — dirty detection re-rendered the directory to compare strings, but rendering allocates fresh entry ids, making every unchanged buffer appear modified and blocking g.. It now computes changes against the existing Oil snapshot.
    • Plugin: src/oil/manager.ts
  • Oil path yanks update the unnamed registery. now writes the selected path to the bundled Vim engine's unnamed register as well as the system clipboard.
    • Plugin: src/oil/manager.ts, src/oil/keybindings.ts
  • g- and g+ navigate the undo tree again — both actions captured this.undoTree in a local at registration time, but activateUndoTreeForFile() swaps that field per note, so the capture was orphaned on the first file activation while edits kept recording into the live tree through buildUndoTreeExtension(). g- walked the empty orphan and returned at its if (!node) guard, doing nothing; where the orphan held history it applied those change sets and restored unrelated content. Both registration paths now resolve the field at call time.
    • Plugin: src/main.ts
  • Renderer crash on Neovim RPC disconnectdisconnectChild() sent nvim_command qa! and then awaited the child's 'close' event, with a SIGKILL fallback, all inside the teardown path. That crashed Obsidian's renderer with a SIGSEGV on a corrupted V8 compressed pointer, reproducible in the CI container at 24 of 46 runs of a spec that cycles connect-traffic-disconnect 14 times. The crash requires RPC traffic and a disconnect: 216 non-RPC tests, 432 traffic-free connect cycles, and 2,400 toggle-free requests were all clean, and stubbing the extmark, float, buffer-line, and cursor handlers changed nothing, so it was never the message handling. Teardown now detaches from the child, unrefs it, and defers SIGTERM with a SIGKILL escalation to a later turn; nothing is awaited and both timers re-check exitCode/signalCode. This is a mitigation, not a cure — arms that drop the synchronous quit-and-wait total 7 of 38 runs against 24 of 46 for the rest (Fisher p = 0.002, roughly a 3x reduction), and a residual path survives. See known limitations.
    • Plugin: src/rpc/neovim-connection.ts
  • Renderer crash from retained tree-sitter nodesgetAllNodesOfType() collected Node objects during a tree walk and read them afterwards, and headingLevelFromNode() called node.child() on those retained nodes. A Node is a JavaScript object holding an address into WASM linear memory, so any parse that grows that memory replaces the backing buffer and leaves the retained node pointing into a detached one — a read at base + garbage, which segfaults the renderer with no JavaScript frame. Structural heading motions (]h, [h, level variants) now walk with a TreeCursor and extract plain data, retaining no node and deleting the cursor. Measured against an isolated reproducer: 8 of 16 runs before, 0 of 16 after; the full spec went from a pooled 29% to 0 of 16.
    • Plugin: src/treesitter/js-api.ts, src/motions/headings.ts
  • vim.treesitter.query.parse() leaked its compiled query — every call built a QueryWrapper that no cache owned, so NamedQueries.dispose() never saw it and the underlying WASM query survived for the life of the Lua state. Parsed queries are now tracked and deleted on state close alongside the named ones, through runCleanups so one failing disposer cannot skip the rest.
    • Plugin: src/lua/treesitter/query-api.ts
  • Query capture nodes carried no tree referenceQuery:iter_captures() and Query:iter_matches() pushed nodes without a _tree field, so node:tree() returned nil and every node reached through :parent()/:child() lost the reference too, unlike Neovim. Both iterators now hold the source node's tree table in the Lua registry and attach it to each capture, matching the existing iter_children shape and releasing the reference when the iterator is exhausted.
    • Plugin: src/lua/treesitter/query-api.ts
  • The 3-second deferred Neovim shutdown is removed — it existed only as a mitigation for the renderer crash resolved below, adopted on a comparison that is now known to have been confounded because both of its arms contained the leak. With the leak fixed, the reproducer spec measures 0 segfaults in 16 runs in all three teardown shapes — the deferral, the original synchronous qa!-and-wait that used to crash 24 of 46, and the immediate non-blocking SIGTERM that now ships. 48 runs, 0 segfaults. Teardown keeps the non-blocking shape, which is better on its own merits and is also less code than the synchronous version: disconnect returns immediately instead of blocking for up to four seconds, and SIGTERM lets Neovim exit on its own terms where qa! did not.
    • Plugin: src/rpc/neovim-connection.ts
  • Intermittent renderer crash when disconnecting the Neovim backend — resolved, and by a fix that had already shipped for an apparently unrelated defect. The cause was a leaked web-tree-sitter TreeCursor: getAllNodesOfType allocated one per structural motion and never deleted it, leaving it in the library's FinalizationRegistry, which registers a cursor under its tree's pointer. The CM6 bridge frees that tree on the next re-parse, so GC later fired the finalizer against a dangling pointer and corrupted the WASM allocator from a GC callback — which is why it required RPC traffic and bundled-fork editing in the same session. The reproducer spec that measured 24 of 46 runs now measures 0 segfaults in 16. The deferred-teardown mitigation is retained but is no longer load-bearing; the measurement that justified it was confounded, because both of its arms contained the leak.
    • Docs: KNOWN_LIMITATIONS.md, test/flaky-inventory.md
  • Two trees handed to Lua had no ownervim.treesitter.get_string_parser() and TSTree:copy() each allocate a tree that no cache holds, so nothing freed them deterministically and their only reclaim was a GC finalizer at an unpredictable moment. That is the mechanism behind unpredictable TSNode staleness, and it is the same handle-ownership class as the renderer crash. Both are now tracked and deleted when the Lua state closes.
    • Plugin: src/lua/treesitter/api.ts, src/lua/treesitter/tree.ts
  • The Lua treesitter parser caches outlived the Lua stateparserCache and ltreeCache were module-level, so lua_close left both populated: a reloaded configuration inherited the previous state's trees, and their WASM trees, parsers, and injection queries were never freed. Both are now per-state and disposed through registerStateCleanup, deleting cached trees and destroying cached LanguageTrees.
    • Plugin: src/lua/treesitter/api.ts

Tests

  • test/ now passes full strict type-checkingtypecheck:tests runs tsc -p tsconfig.test.json unfiltered at zero errors, replacing the resolution-only allowlist (TS2304, TS2305, TS2307, TS2551, TS2552) that let nullability, argument-type, and missing-property errors through. The 19 errors the run reported against src/ were configuration gaps rather than defects and are resolved by including the existing ambient declarations; no src/ code changed. The pass surfaced four defects no other gate could see: a vacuous expect(result.flash).toBe(DEFAULT_SETTINGS.flash) comparing undefined to undefined against a setting that does not exist, a runCleanups() call missing its required context argument, a vimHandleKeys(string[]) call whose thrown TypeError was swallowed by the diagnostic's own catch, and a Probe interface missing the two fields its assertions read.
    • Tests: tsconfig.test.json, package.json, test/unit/settings-resolution.test.ts, test/unit/lua/plugin-demand-harness.ts, test/specs/navigation.e2e.ts, test/specs/lua-plugin-flash-diagnostic.e2e.ts, and 42 further spec and unit files
  • The vim.treesitter e2e suite now tests something — all 12 of its assertions read getLuaConfigError(), which returned plugin.luaLoadResult.error. No such member exists on the plugin, so the helper returned null unconditionally and the whole suite passed against a Lua body of this is not valid lua @@@ ###; the vim.g.__* flags each test computed were never read. Each test now reads its flags back and asserts their values, and a config that fails part-way through drops the later flags rather than reporting nothing. Sabotaging the same test now fails it.
    • Tests: test/specs/treesitter.e2e.ts
  • Spec shape guardstest/unit/spec-shape-guards.test.ts closes the class strict typing cannot: a hand-written Record<string, T> plugin handle is an assertion about the plugin, so an invented member compiles and silently reads undefined. It cross-checks every declared plugin member in test/ — 97 spec files declare one — against the class in src/main.ts, and fails any executeObsidian callback whose { error: … } branch is cast to a primitive that drops it (109 such callbacks exist; none currently does). Both guards are negative-controlled.
    • Tests: test/unit/spec-shape-guards.test.ts
  • Root configuration files are type-checkedtypecheck:config adds a seventh blocking gate over wdio.conf.mts, eslint.config.mts, vitest.config.ts, and the .mjs build, release and CI-reporting scripts, none of which sat inside either existing tsconfig include. wdio.conf.mts reported 22 errors on first measurement, including in the afterTest cleanup hook whose failure surfaces as cross-test flakiness rather than a red test. It needs moduleResolution: nodenext and type-only imports of webdriverio and wdio-obsidian-service, because a declare global augmentation only loads if its package is imported. The .mjs half reported 11 more: esbuild.config.mjs was clean, but report-e2e-failures.mjs — the script that reports CI failures — carried an implicit any[], four unchecked string | undefined arguments and an unguarded Map.get() dereference, and version-bump.mjs read process.env.npm_package_version without a guard, so running it outside npm version wrote a version-less manifest.json and a literal "undefined" key into versions.json. All three scripts were re-run against sample input to confirm unchanged output.
    • Tests: tsconfig.config.json, wdio.conf.mts, vitest.config.ts, esbuild.config.mjs, version-bump.mjs, scripts/report-e2e-failures.mjs, scripts/report-latency.mjs, package.json
  • M7 latency certification harnesstest/specs/rpc-latency.e2e.ts measures real keydown to first rAF after the same CM6 transaction criterion for fork and production RPC over a 2,004-line runtime fixture (N=500 plus 75 warmups). It proves production fork interception and bridge engagement, enforces stable document size, and gates on the expected fork-faster p50 relationship. The certified run measured fork/RPC p50 15.3/17.3 ms, p95 39.5/36.2 ms, and p99 53.1/48.7 ms, for p95/p99 deltas of −3.3/−4.4 ms; delay, forced-layout, engagement, and drift controls are recorded in rpc-latency-negative-controls.md.
  • RPC prerequisite coverage — all 13 RPC specs use test/specs/rpc-prerequisites.ts to warn and skip when Neovim is absent, below API level 12, or a required fetched fixture is missing. Oil now checks its fetched flash fixture, and only the POSIX old-API stub scenario skips on Windows.
  • RPC lifecycle acceptance coverage — 7 WDIO scenarios cover attach/API reporting, runtime disable, Vim disable, unexpected SIGKILL and reconnect, plugin unload, missing binaries, and the API-level floor. PID liveness is checked directly, teardown/version-floor sabotages are recorded in test/specs/rpc-lifecycle.negative-control.md, and the settings-option inventory records both controls as process-backend settings rather than Vim options. Disposable .sisyphus/ spike files are excluded from the production lint project.
  • RPC text synchronisation acceptance coveragetest/specs/rpc-text-sync.e2e.ts checks isolated fixture loading, normal Markdown buffer identity, 210 boundary-valid API edits, cross-line collapse, end/start deletion, append, and the documented invalid UTF-8 boundary divergence against a raw-byte Lua oracle. Its active-leaf regression uses two known file contents, switches both ways, retains a Neovim key edit, and independently reads both files through the vault adapter. Restoring the one-shot seed reproduced Target.md as A original\nA second line on first activation and rpc-A original\nA second line after the later switch, including on disk.
  • RPC key delegation acceptance coveragetest/specs/rpc-keys.e2e.ts drives 210 insert/operator/visual/count/dot-repeat/undo-redo/macro sequences containing ASCII, astral, combining, and CJK text through the editor DOM, compares ciwfoo<Esc>w. with live headless Neovim, checks bridged/unbridged buffer/cursor/mode/register identity, proves single insertion, and records the visible/source D7 measurement. Negative controls produced xxseed without fork interception, xxiseed without preventDefault, CM column 0 instead of 6 without cursor sync, and buffer/register divergence after a bridged-only x.
  • RPC frontmatter acceptance coverage — six M2c scenarios in test/specs/rpc-keys.e2e.ts cover visible-mode connection, repeated upward motion, frontmatter-preserving dd with an independent vault read, guarded gg, Source-mode navigation, and API edits inside the fold. test/specs/rpc-keys-negative-controls.md records the no-fold, no-guard, and Source-fold sabotages and exact cursor/document outcomes.
  • RPC decoration acceptance coveragetest/specs/rpc-decorations.e2e.ts compares every rendered flash.nvim label against flash's own all-namespace extmark query, verifies label selection in Neovim and CM6, statically excludes polling and grid consumption, and proves the idle bridge is empty. Negative controls observed 39/40 labels after dropping one forwarded extmark, all 40 offsets shifted by one after a byte-column error, and 0/40 decorations without the UI redraw clock.
  • RPC write/read acceptance coveragetest/specs/rpc-write-routing.e2e.ts verifies Obsidian's save command was invoked, reads saved and deliberately stale content through the vault adapter, checks :e! preserves the unsaved editor document, and rejects a stale Neovim modified flag. test/specs/rpc-write-routing-negative-controls.md records the four required sabotages and observed values.
  • RPC Obsidian bridge acceptance coveragetest/specs/rpc-obsidian-bridge.e2e.ts covers seven representative host scenarios plus teardown-aware refresh. test/specs/rpc-obsidian-bridge-negative-controls.md records isolated missing-map, no-op-dispatch, unguarded-abbreviation, and skipped-teardown failures.
  • M4b registry and workspace bridge coveragetest/unit/vim-registration-inventory.test.ts guards the measured 75-motion, 78-action, 164-map, and 102-ex registration surface and six M4a selections. The RPC bridge spec adds horizontal split, four-way pane focus, counted and explicit tab targeting, lowercase command/abbreviation safety, wikilink definition navigation, and expanded refresh inventory coverage.
  • M4b Batch 2 picker bridge coverage — the RPC bridge spec asserts every picker leader source, lowercase :buffers, query-bearing :grep, named :Picker source selection, modal-key file selection plus post-selection Neovim content, guarded substitution behavior, and duplicate-free refresh. Four subject sabotages prove query, mapping, guard, and re-seed assertions fail independently.
  • M4b Batch 3 Harpoon, marks, and jumplist coverage — the RPC bridge spec asserts slot mappings and arguments, optional versus explicit removal, three-file cursor-preserving cycling, cross-note and counted older jumps with independent file/cursor/content checks, :jumps, mark-table plus sign-column deletion, substitution safety, and duplicate-free refresh. Four subject sabotages prove slot, host ownership, count, and gutter refresh independently.
  • M4b Batch 4 Oil isolation coveragetest/specs/rpc-oil.e2e.ts drives all 16 Oil mappings through the embedded editor's real DOM with RPC connected, verifies 14 observable host effects, explicitly skips the two OS-shelling actions, checks handler/interception state on Oil and Markdown, and proves Oil keys do not change Neovim's buffer. Forced interception removed the first character from Neovim's sentinel, while no-op'ing oilHelp failed only its scenario.
  • M4b Batch 5 folding and undo-tree coveragetest/specs/rpc-folds-undo.e2e.ts covers nine scenarios for the fold mirror, native fold motions/operator, raw-byte undo/redo and chronological navigation, native :earlier/:later, Neovim-backed sidebar data, and duplicate-free bridge refresh. Four subject sabotages prove fold forwarding, row mapping, sidebar data ownership, and command lifecycle independently.
  • M5a structural navigation and hard-wrap coveragetest/specs/rpc-structural-nav.e2e.ts runs 14 fork-oracle parity scenarios across headings, levels, counts, list items, links, gq/gw, and seven operator-pending forms. It compares yank contents, rejects unexpected empty documents, and independently reads one edited note through the vault adapter. Five subject sabotages are recorded in rpc-structural-nav-negative-controls.md.
  • M5b Markdown text-object coveragetest/specs/rpc-text-objects.e2e.ts runs 65 fork-oracle parity scenarios covering delete, change, yank/register, visual selection, and counted forms for 13 Markdown object shapes, including nested and adjacent delimiters. Every operator checks the document-wipe invariant, one edit is read through the vault adapter, and three subject sabotages are recorded in rpc-text-objects-negative-controls.md.
  • M6a floating-window coveragetest/specs/rpc-floats.e2e.ts compares flash.nvim's prompt content and config-derived placement with Neovim, checks two-float z-index order, close and disconnect cleanup, and a custom float's extmark and border. rpc-floats-negative-controls.md records missing-forwarding, one-cell offset, ignored-z-index, and stale-close failures.
  • M6b IME composition coveragetest/specs/rpc-ime.e2e.ts drives Chromium's native composition path through CDP, verifies byte-exact CJK commit, dot-repeat, cancellation, key suppression, and active-note switching with an independent vault-adapter read. rpc-ime-negative-controls.md records CM6-only commit, buffer-API commit, and composing-key forwarding failures.
  • M8a external-UI message coveragetest/specs/rpc-messages.e2e.ts covers eight scenarios for informational and error Notices, real-key Lua errors, silent undo/search kinds, one-per-message dispatch, duplicate limiting, and 200-key grid-event latency. rpc-messages-negative-controls.md records missing-dispatch, noisy-kind, and removed-dedup failures with observed counts and values.
  • M8b external command-line coveragetest/specs/rpc-cmdline.e2e.ts drives all 11 command-line, caret, prefix, prompt, selection, cancellation, nesting, and bundled-fork-isolation scenarios through real editor key events. rpc-cmdline-negative-controls.md records stale-hide, raw-byte-caret, and single-level-state failures with observed counts and values.
  • M8c popup-menu and M8d status-mode coveragetest/specs/rpc-popupmenu.e2e.ts drives insert completion and command-line wildmenu selection/hide through real editor key events, while four lifecycle scenarios cover insert, normal, visual-line, and disconnect arbitration. rpc-popupmenu-negative-controls.md records ignored-selection, wrong-anchor, and suppressed-mode-handler failures with observed counts and values.
  • The treesitter-handle-leak rule's own behaviour is locked by a testtest/unit/treesitter-handle-leak-rule.test.ts runs the rule against nine fixtures and asserts exactly which fire: all five leak shapes, none of the four correctly-freed ones, plus the shipped getAllNodesOfType defect. Negative-controlled by restoring the stopBy: end that produced the original false positive, which reports [ 'reassign-both.ts' ] against an expected []. A blocking gate that flags correct code gets disabled, so the silent cases matter as much as the firing ones.
  • Unowned treesitter handle coveragetest/unit/lua/treesitter-queries.test.ts asserts that a get_string_parser() tree and a TSTree:copy() are both deleted when the Lua state closes. Negative-controlled against the unowned state: 3 deletes with the owner installed, 1 without, the survivor being the document parser's cached tree.
  • Treesitter Lua memory-safety coveragetest/unit/lua/treesitter-queries.test.ts adds three regressions against the real bundled grammars: query.parse() wrappers are deleted on state close, capture nodes from both iterators expose their tree, and the parser cache is freed on close without leaking into the next state. Each was negative-controlled against the defect it replaces — 0 deletes instead of 3, iter_captures node has no tree and iter_matches node has no tree, and 1 delete instead of 2.
  • Undo-tree navigation is asserted behaviourally — the existing g-/g+ scenarios asserted only that the keys did not crash and left the mode alone, both of which held for the entire time g- was broken. test/specs/undo-tree.e2e.ts now asserts that the live tree's current sequence moves, which fails against the previous code with the sequence unchanged at 20 instead of 19 while the two non-crash scenarios still pass.
  • The pluginAutoFetch boundary is now held by a test rather than by an argument — the M0 policy determination rests on that fetch path never feeding the real Neovim runtime, since installing executable dependencies for a runtime is listed under Not allowed in the Developer Policies, and design risk R-5 required a test asserting it cannot. test/unit/rpc/plugin-autofetch-boundary.test.ts asserts the complete spawn argv, so any added runtimepath entry fails it, and separately asserts that no file under src/rpc/ imports the fetch or store modules or names their on-disk paths, which it reads from plugin-store.ts rather than restating. Negative-controlled twice: prepending lua/.staging to the runtimepath failed three of the five assertions, reporting a 7-element argv against the expected 5; adding a plugin-store import to decorations.ts failed exactly one, reporting [ 'decorations.ts -> plugin-store' ] against [].
    • Tests: test/unit/rpc/plugin-autofetch-boundary.test.ts, src/rpc/neovim-connection.ts
  • Unit tests can now import any RPC module — anything reaching src/rpc/companion.lua failed to load under Vitest, which parsed the Lua as JavaScript, so no test under test/unit/rpc/ could import neovim-connection.ts at all. A luaTextPlugin() mirrors esbuild's '.lua': 'text' loader, alongside the existing .wasm precedent.
    • Tests: test/helpers.ts, vitest.config.ts
  • Table navigation under the Neovim backend is coveredtest/specs/rpc-table-nav.e2e.ts asserts the overlay activates while RPC is connected, that l moves the highlighted cell, and that neither Neovim's cursor nor the buffer changes. The two absence assertions carry a positive control rather than a sabotage: a sibling scenario presses the same key outside the table and requires the same probe to report movement, so "Neovim did not move" cannot hold for every implementation. Negative-controlled by forcing canActivate() false, which reported entered: false, navigated: false against true, true.
    • Tests: test/specs/rpc-table-nav.e2e.ts
  • Companion-label and leader-group coverage — six unit cases cover leaderGroupPrefixes and leaderGroupLabel, and two live scenarios assert the companion's operator-pending maps read as prose and that every registered group starts with the leader without ever equalling it. Negative-controlled: starting the prefix loop at leader.length instead of leader.length + 1 failed three unit cases including the leader-exclusion one; removing the install call failed the live group scenario; renaming the group-label key failed the label case.
    • Tests: test/unit/rpc/bridge-action-label.test.ts, test/specs/rpc-obsidian-bridge.e2e.ts
  • Hint-mode and bridged-label coveragetest/unit/rpc/bridge-action-label.test.ts covers the label derivation across 13 names; two scenarios in rpc-obsidian-bridge.e2e.ts activate hint mode from Neovim and assert the buffer is byte-identical afterwards, and that no generated desc leaks an internal id. The buffer assertion is the load-bearing one — the overlay closing would pass even if the label key had also reached Neovim. Negative-controlled: removing hintactivate from the allowlist failed only the hint scenario; reverting DESC_PREFIX to the old id form failed three, including the refresh-inventory check. The refresh check now identifies leader mappings by their leader prefix rather than by desc, since the description no longer encodes an action name.
    • Tests: test/unit/rpc/bridge-action-label.test.ts, test/specs/rpc-obsidian-bridge.e2e.ts
  • External mode coveragetest/unit/vim/external-mode.test.ts covers the Neovim mode() mapping across 19 values plus the publish, dedup and clear semantics, and asserts the precedence rule directly rather than mirroring it. Two lifecycle scenarios drive real keys against a live Neovim for insert, normal, visual and visual-line, and check the seam clears on disconnect. Negative-controlled four ways: testing no after n failed the three operator-pending cases; removing the unchanged-mode dedup failed exactly one; dropping the visual-block mapping failed three; reversing ?? in the precedence helper failed two; and removing the per-key publish failed both e2e scenarios.
    • Tests: test/unit/vim/external-mode.test.ts, test/specs/rpc-lifecycle.e2e.ts
  • A bundled snippet was referenced with mixed path separators on Windows — the relative path is built with a literal / and was then joined to the configuration directory with the platform separator, so the file was written to …\lua\vim-motions-snippets\global.json and referenced as …\lua\vim-motions-snippets/global.json. Windows opens either spelling, so nothing failed at runtime and the snippet still expanded; the only symptom was two spellings of one path. Relative segments are now split on either separator before rejoining, and a unit case pins it so the mismatch fails on every platform rather than on a Windows shard.
    • Plugin: src/rpc/config-export.ts
  • A Windows-only assertion failure in the snippet-export spec — the scenario compared the generated configuration against a raw filesystem path, but the path is embedded in a Lua string literal, so on Windows every separator is doubled and D:\a\… never appears verbatim. It passed on Linux and macOS and failed one Windows shard. The product was correct throughout, which the sibling scenario proved by expanding a snippet through LuaSnip on the same runner; only the assertion was wrong. It now unescapes the literals before comparing, and a platform-independent unit case pins the round-trip so the next regression of this class fails everywhere rather than on one shard.
    • Tests: test/specs/rpc-config-export.e2e.ts, test/unit/rpc/config-export.test.ts
  • Snippet expansion is covered end to end under the Neovim backendLuaSnip joins test/fixtures/test-plugins.json pinned at 0abc8f39, which makes it require-able from a live Neovim through the same runtimepath append that already serves flash.nvim to the decoration and float oracles. The scenario drives the production path rather than the API: real keys into the Obsidian editor, forwarded to Neovim, expanded by LuaSnip through the generated configuration's own <Tab> mapping, and mirrored back. It skips with a message when the fixture is absent. Removing the expansion key from the generated block fails it, along with the two scenarios that load the configuration.
    • Tests: test/fixtures/test-plugins.json, test/specs/rpc-config-export.e2e.ts, .gitignore
  • Consent-modal and auto-regeneration coverage — three scenarios in rpc-config-export.e2e.ts drive the real settings UI: the preview lists repositories and the configuration before anything happens and cancelling writes nothing, auto-regeneration rewrites on a covered change only while the toggle is on, and a hand-edited file survives even with it on. First e2e in the suite to open the settings tab; post-1.13 renders the seven pages as navigable rows, so General has to be opened before the controls exist in the DOM, and the settings pane is itself a modal, so selecting "the first .modal" finds it rather than the preview. Negative-controlled three ways: forcing the auto-refresh gate open failed only the toggle scenario, neutering the sentinel failed both hand-edited-file scenarios, and making Cancel write failed the consent scenario. The first attempt at that last control had Cancel call the full install path and did not fail, because the write lands long after any window a test can wait on — the assertion covers the write rather than the install, and says so.
    • Tests: test/specs/rpc-config-export.e2e.ts
  • Snippet-export coverage — unit cases assert the block emits the paths it was given and registers an expansion key, and a live scenario asserts the bundled files are written where the generated config says they are. The expansion-key control initially did not fire: the assertion looked for expand_or_jumpable, which survives on the following line when the vim.keymap.set call is removed, so it was strengthened to assert the mapping itself. Removing the bundled write failed only the live scenario.
    • Tests: test/unit/rpc/config-export.test.ts, test/specs/rpc-config-export.e2e.ts
  • Package-activation and install-list coveragerpc-config-export.e2e.ts asserts the real spawn leaves Neovim able to activate a package, since the --clean failure looks like success and cannot be caught by inspecting a hand-built command line; removing the packpath restore fails it. Unit cases cover the repository list, including that flash is not requested twice when both features that use it are on. The R-5 argv assertion caught the new spawn element exactly as designed, and rather than bumping the expectation it now also asserts the added path resolves inside Neovim's own data directory, which is what keeps it clear of the vault fetch store.
    • Tests: test/unit/rpc/plugin-autofetch-boundary.test.ts, test/unit/rpc/config-export.test.ts, test/specs/rpc-config-export.e2e.ts
  • Configuration-export coveragetest/unit/rpc/config-export.test.ts holds generation, the sentinel, the fingerprint and the module probe across 17 cases; test/specs/rpc-config-export.e2e.ts proves the written file is requireable by a live Neovim rather than inspecting the path we chose. Negative-controlled five ways: removing the sentinel check failed only the overwrite-refusal case; dropping the pcall guard failed two generation cases; making the fingerprint constant failed staleness; and dropping the lua/ segment failed three e2e cases. The fourth control initially did not fire at all, because resolveGeneratedConfigPath has two branches and the first sabotage hit the one the spec does not take — the control is what caught that, not review.
    • Tests: test/unit/rpc/config-export.test.ts, test/specs/rpc-config-export.e2e.ts, test/unit/known-set-options.test.ts
  • Editor-option projection coveragetest/specs/rpc-editor-options.e2e.ts drives five scenarios through real editor keydowns rather than nvim_input, covering bullet continuation on and off, yank highlight on and off, and reapplication after an activation reseeds the buffer. Negative-controlled three ways: restoring the ftplugin's f flag produced hello against - hello; removing the o/O mappings produced the same; suppressing the yank notification failed only the highlight scenario; and dropping the reseed call produced hello with no marker while the other four passed. A fourth control was not needed — rpc-structural-nav already failed on the first implementation, reporting gq output re-bulleted as - to wrap with a hanging indent instead of the hanging-indent form.
    • Tests: test/specs/rpc-editor-options.e2e.ts
  • Generic Obsidian-command coveragetest/specs/rpc-obsidian-bridge.e2e.ts runs :ob workspace:toggle-pin through Neovim and asserts the leaf pins and unpins, having first confirmed the id is in Obsidian's registry and that no bridge-installed command could account for the effect. A second scenario holds the abbreviation guard. Negative-controlled in both halves: dropping 'ob' from the allowlist failed only that scenario with obInstalled: false against true, and neutering executeCommand failed it again at the pin wait while the guard scenario still passed. The bridge-installed probe is scoped by the vim-motions-rpc: marker because matching every Neovim user command picked up the developer config's DapStepInto, which lowercases to contain pin.
    • Tests: test/specs/rpc-obsidian-bridge.e2e.ts

Documentation

  • README.md, docs/configuration/settings.md: desktop/arbitrary-code/FFI/external-file/no-sandbox/no-install disclosure and Milestone 1 scope.
  • KNOWN_LIMITATIONS.md: current lifecycle-only boundary and separation from fengari plugin auto-fetching.
  • AGENTS.md, CONTRIBUTING.md: RPC source ownership and lifecycle test locations.
  • README.md, KNOWN_LIMITATIONS.md, docs/features/neovim-backend.md: M8a message routing, duplicate limiting, and deferred command-line/popup rendering.
  • AGENTS.md, CONTRIBUTING.md: M8a redraw/message source ownership and acceptance/negative-control locations.
  • README.md, KNOWN_LIMITATIONS.md, docs/features/neovim-backend.md: M8b command-line, prompt, byte-caret, nested-level behavior, and deferred popup-menu boundary.
  • AGENTS.md, CONTRIBUTING.md: M8b command-line source ownership and acceptance/negative-control locations.
  • README.md, KNOWN_LIMITATIONS.md, docs/features/neovim-backend.md: M8c popup-menu completion and M8d Neovim-owned status-bar mode behavior.
  • AGENTS.md, CONTRIBUTING.md: popup-menu/mode-status source ownership and acceptance/negative-control locations.
  • CHANGELOG.md: M8c/M8d implementation, tests, and documentation coverage.
  • CHANGELOG.md: Milestone 1 implementation and test coverage.
  • README.md, docs/configuration/settings.md, KNOWN_LIMITATIONS.md: Milestone 2a text scope, active-editor/single-buffer boundary, and deferred key/frontmatter/decorations work.
  • AGENTS.md, CONTRIBUTING.md: document-sync source ownership and text-sync acceptance/negative-control locations.
  • test/specs/rpc-text-sync-negative-controls.md: observed line-event, offset, trailing-newline, decoded-oracle, and intro-buffer identity failures.
  • CHANGELOG.md: Milestone 2a implementation and test coverage.
  • README.md, docs/configuration/settings.md, KNOWN_LIMITATIONS.md: explicit Neovim configuration selection, isolated startup behavior, production default, and measured startup benefit.
  • AGENTS.md, CONTRIBUTING.md: fixture-backed RPC test contract and configuration-aware connection ownership.
  • README.md, docs/configuration/settings.md, KNOWN_LIMITATIONS.md: Milestone 2b key ownership, Source-frontmatter requirement, cursor/mode synchronization, and deferred IME/decorations scope.
  • AGENTS.md, CONTRIBUTING.md: key-delegation source ownership and RPC key acceptance coverage.
  • .sisyphus/plans/neovim-rpc-backend-design.md: D7 decision and measured visible/source cursor behavior.
  • CHANGELOG.md: Milestone 2b implementation, tests, and negative controls.
  • KNOWN_LIMITATIONS.md, .sisyphus/plans/neovim-rpc-backend-design.md: corrected per-activation seeding semantics and explicit prohibition on writing a stale mirror into another note.
  • AGENTS.md, CONTRIBUTING.md: document-sync ownership and the independent two-file disk-integrity regression.
  • test/specs/rpc-text-sync-negative-controls.md: exact editor and on-disk values from restoring the cross-note overwrite defect.
  • CHANGELOG.md: cross-note data-loss fix and negative-control evidence.
  • README.md, docs/configuration/settings.md, KNOWN_LIMITATIONS.md: Milestone 2c support for both properties modes and rendered-frontmatter behavior.
  • AGENTS.md, CONTRIBUTING.md: shared delimiter, RPC fold ownership, cursor guard, widget-focus exclusion, and acceptance coverage.
  • test/specs/rpc-keys-negative-controls.md: M2c fold, guard, and Source-mode negative-control evidence.
  • CHANGELOG.md: Milestone 2c implementation, tests, and documentation.
  • README.md, docs/configuration/settings.md, KNOWN_LIMITATIONS.md: Milestone 3 decoration scope, persistent-extmark limits, redraw clock, and remaining float/IME boundaries.
  • AGENTS.md, CONTRIBUTING.md: bundled companion/decorations ownership and RPC decoration acceptance coverage.
  • .sisyphus/plans/neovim-rpc-backend-design.md: resolved D-E companion packaging decision.
  • test/specs/rpc-decorations-negative-controls.md: exact missing-label, shifted-offset, missing-redraw-clock, and assertion-reachability failures.
  • CHANGELOG.md: Milestone 3 implementation, tests, and negative controls.
  • README.md, docs/configuration/settings.md, KNOWN_LIMITATIONS.md: acwrite save ownership and Obsidian-backed reload semantics.
  • AGENTS.md, CONTRIBUTING.md: companion/document-sync ownership and write/read acceptance coverage.
  • .sisyphus/plans/neovim-rpc-backend-design.md: deferred M2a write/read item completion and verification evidence.
  • test/specs/rpc-write-routing-negative-controls.md: exact missing-autocmd, missing-acwrite, stale-disk, and dirty-flag failures.
  • CHANGELOG.md: write/read routing implementation, tests, and documentation.
  • README.md, docs/configuration/settings.md, KNOWN_LIMITATIONS.md: M4a representative surface, guarded lowercase commands, and remaining M4b boundary.
  • AGENTS.md, CONTRIBUTING.md: feature-bridge source ownership and acceptance/negative-control locations.
  • test/specs/rpc-obsidian-bridge-negative-controls.md: exact M4a mapping, dispatch, abbreviation, and teardown sabotage outcomes.
  • CHANGELOG.md: M4a implementation, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/features/workspace-navigation.md: M4b Batch 1 workspace/navigation scope, parameter forwarding, and remaining batches.
  • AGENTS.md, CONTRIBUTING.md: expanded bridge ownership and registry inventory/RPC acceptance coverage.
  • test/specs/rpc-obsidian-bridge-negative-controls.md: exact M4b count, pane-map, abbreviation-guard, and refresh-teardown failures.
  • .sisyphus/plans/neovim-rpc-backend-design.md: M4b Batch 1 implementation and verification result.
  • CHANGELOG.md: M4b Batch 1 implementation, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/features/ex-commands.md: M4b Batch 2 picker scope, argument forwarding, modal ownership, and remaining batches.
  • AGENTS.md, CONTRIBUTING.md: expanded RPC picker acceptance coverage.
  • test/specs/rpc-obsidian-bridge-negative-controls.md: exact Batch 2 query, mapping, abbreviation, and re-seed sabotage outcomes.
  • .sisyphus/plans/neovim-rpc-backend-design.md: M4b Batch 2 implementation and empirical result.
  • CHANGELOG.md: M4b Batch 2 implementation, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/features/harpoon.md, docs/features/workspace-navigation.md: M4b Batch 3 scope, host-owned cross-note jumplist, cursor restoration, and uppercase cross-file mark gap.
  • AGENTS.md, CONTRIBUTING.md: expanded feature-bridge ownership and RPC acceptance coverage.
  • test/specs/rpc-obsidian-bridge-negative-controls.md: exact Batch 3 slot, jumplist ownership/count, and gutter-refresh failures.
  • .sisyphus/plans/neovim-rpc-backend-design.md: M4b Batch 3 implementation and explicit marks/jumplist ownership decision.
  • CHANGELOG.md: M4b Batch 3 implementation, fix, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/features/oil-explorer.md: M4b Batch 4 native Oil ownership, RPC exclusion, and path-register behavior.
  • AGENTS.md, CONTRIBUTING.md: RPC Oil acceptance and negative-control locations.
  • test/specs/rpc-oil-negative-controls.md: exact forced-interception corruption and isolated action failure.
  • .sisyphus/plans/neovim-rpc-backend-design.md: corrected Class-B total and empirical Oil result.
  • CHANGELOG.md: native Oil fixes, RPC isolation tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/features/workspace-navigation.md, docs/features/undo-tree.md: Batch 5 fold/undo ownership, rendering, sidebar data, and fold-persistence boundary.
  • AGENTS.md, CONTRIBUTING.md: RPC fold/undo source ownership and acceptance/negative-control locations.
  • test/specs/rpc-folds-undo-negative-controls.md: exact forwarding, row-mapping, sidebar-source, and missing-command failures.
  • .sisyphus/plans/neovim-rpc-backend-design.md: corrected Class-B total from 121 to 80 and per-endpoint Batch 5 disposition.
  • CHANGELOG.md: Batch 5 implementation, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/reference/keybindings.md, docs/features/structural-navigation.md, docs/features/hardwrap.md: M5a native/ported ownership, aliases, operator parity, and textwidth wiring.
  • AGENTS.md, CONTRIBUTING.md: M5a RPC acceptance and negative-control locations.
  • test/specs/rpc-structural-nav-negative-controls.md: exact missing-map, missing-width, level, motion-kind, and count failures.
  • CHANGELOG.md: M5a implementation, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/reference/keybindings.md, docs/features/text-objects.md: M5b native/ported ownership, supported object list, bounded ranges, and the unparsed highlight-delimiter gap.
  • AGENTS.md, CONTRIBUTING.md: M5b RPC acceptance and negative-control locations.
  • test/specs/rpc-text-objects-negative-controls.md: exact range-end, missing-map, and count-forwarding failures.
  • CHANGELOG.md: M5b implementation, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/configuration/settings.md: M6a float support, approximate CM6 cell mapping, relative origins, and remaining IME boundary.
  • AGENTS.md, CONTRIBUTING.md: floating-window source ownership and RPC acceptance/negative-control locations.
  • test/specs/rpc-floats-negative-controls.md: exact forwarding, position, z-index, and stale-close sabotage outcomes.
  • .sisyphus/plans/neovim-rpc-backend-design.md: M6a implementation and empirical result.
  • CHANGELOG.md: M6a implementation, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md, docs/configuration/settings.md: M6b composition ownership, commit path, cancellation lifecycle, and remaining RPC boundaries.
  • AGENTS.md, CONTRIBUTING.md: IME input source ownership and CDP acceptance/negative-control locations.
  • test/specs/rpc-ime-negative-controls.md: exact CM6-only, buffer-API, and composing-key forwarding sabotage outcomes.
  • .sisyphus/plans/neovim-rpc-backend-design.md: M6b implementation and empirical result.
  • CHANGELOG.md: M6b implementation, tests, negative controls, and documentation.
  • README.md, KNOWN_LIMITATIONS.md: M7's certified latency measurements and deltas.
  • AGENTS.md, CONTRIBUTING.md: production latency harness, p50 sanity gate, negative-control locations, and cross-platform RPC test tooling.
  • .sisyphus/plans/neovim-rpc-backend-design.md: M7 implementation, fold-expression repair, measurements, controls, and certification verdict.
  • test/specs/rpc-latency-negative-controls.md: exact delay, layout, engagement/isolation, size-drift, and sanity-gate output.
  • CHANGELOG.md: M7 harness, fold-expression performance repair, negative controls, and documentation.
  • docs/features/neovim-backend.md, docs/features/index.md: opt-in Neovim backend setup, security and ownership boundaries, shared keybindings, and limitations.
  • docs/configuration/settings.md: removes the stale milestone scope label.
  • README.md, KNOWN_LIMITATIONS.md: removes stale milestone wording while preserving the certified M7 measurements.
  • AGENTS.md, CONTRIBUTING.md: source-tree gaps, pinned installer/workflow ownership, RPC prerequisite guard, and current latency gate.
  • CHANGELOG.md: cross-platform CI provisioning, shared skip behavior, Windows scenario scope, and documentation updates.
  • test/flaky-inventory.md: the WASM heap-move mechanism is refuted by measurement — 84 probe readings across master and the reverted retaining walk, including failing runs, all report zero heap growth. Pre-growing the heap is dropped, and the four places that stated the mechanism as established are corrected. The retained-node fix and its measurements stand; only the stated reason was wrong.
  • KNOWN_LIMITATIONS.md: Lua TSNode handles become stale after a re-parse, and lifetime management is declined rather than deferred. Also records that dropping the delete() calls is not a safe alternative, because web-tree-sitter's own FinalizationRegistry still frees the handle at a GC-determined moment.
  • test/flaky-inventory.md: the renderer segfault's root cause, established by a 2x2. The fix changed two things — it stopped retaining Nodes and added the missing cursor.delete() — and only the second mattered: the leak alone measured 5 of 8 container runs with no nodes retained, while the original retaining walk with the cursor freed measured 0 of 6. A leaked TreeCursor stays in web-tree-sitter's FinalizationRegistry holding its tree's pointer, so GC later frees a tree the bridge already deleted. This also invalidates the arm that retired the use-after-free class: neutralising the twelve in-repo delete() calls never disabled the registry, so "nothing is ever freed" was never achieved.
  • docs/features/neovim-backend.md, KNOWN_LIMITATIONS.md: the Neovim plugin-compatibility boundary is published, closing design risk R-6. Compatibility is decided by mechanism rather than by plugin name: extmarks, virtual text, and floating windows cross, while screenpos() and the screen-cell query class are unbridgeable by construction — the bridge transports buffer coordinates, grid events are discarded, and proportional Markdown typography has no stable cell grid to answer with. matchadd() is unreachable separately, being window-local rather than an extmark. flash.nvim is named as the measured bridged case rather than presented as a certification of any plugin.
  • docs/features/neovim-backend.md, KNOWN_LIMITATIONS.md, README.md: the plugin's own Lua and vimrc config does not bind editor keys while the Neovim backend owns them. .obsidian.init.lua still loads, but a vim.keymap.set there targets the bundled fork, which key delegation stands down, so editor mappings never fire and their equivalents belong in the user's init.lua. Settings and host-rendered features are unaffected, and the bridge's generated Obsidian action mappings follow the configured leader but not arbitrary remaps.
  • docs/features/neovim-backend.md, KNOWN_LIMITATIONS.md: a What changes in RPC mode section records which bundled-engine features Neovim replaces, which need a Neovim equivalent, and which host features read the fork's event stream rather than bridge state. The Class-A disposition was a design decision but had never been written down for users, and the host-feature group was assumed unaffected because it only renders: which-key and hint mode subscribe to the adapter's vim-keypress/vim-command-done, yank highlight to vim-yank, input-method switching to vim-mode-change, and the animated cursor resolves its per-mode shape from adapter.state.vim, so the shape stays on normal while Neovim is in insert. Measured on 0.12.5 rather than assumed: gr is unmapped, and o on - item one yields hello because the stock Markdown ftplugin sets formatoptions=jtcqln. Corrects a claim added earlier in this same section, which listed snippets, which-key, and the animated cursor among features that still apply — Tab is forwarded to Neovim before CM6's snippet keymap sees it.
  • AGENTS.md: re-measures the es2021 build-target rationale, which was argued against the mobile floor "at 1.7.2" and so no longer matched the manifest. The conclusion is unchanged and the basis is now correct: Android 5.1 is the binding constraint and has not moved — still the Play Store minimum at 1.8.10 and at 1.13.6 — so raising minAppVersion does not raise it, while iOS went 13 → 14.5 → 15. At es2022 esbuild emits 8 static {} blocks where es2021 emits none, and those need Safari 16.4, above iOS 15 and far above the Android 5.1 WebView. The saving is 402 bytes gzipped of 729,321, not the 31 recorded; the bundle has grown since. The claims about native class fields and #private methods are dropped as unevidenced — #private occurs identically in both builds and no source file uses it.
  • README.md, docs/getting-started/index.md: state the 1.8.7 minimum, which the manifest had already been raised to.
  • docs/configuration/settings.md: adds the Grep binary mode and Picker keymap rows, which had no documentation at all, and corrects the claim that cursor shapes require the bundled fork.
  • docs/configuration/status-bar.md, docs/getting-started/recommended-setup.md: the extended mode indicators and per-mode cursor shapes were listed as fork-only; both are also provided by the Neovim backend, and only built-in vim mode lacks them.
  • docs/index.md: adds the tags frontmatter every other page carries.
  • KNOWN_LIMITATIONS.md: the async-Lua row and the require() status both still said "deferred" long after src/lua/coroutine-runner.ts and src/lua/module-snapshot.ts shipped.
  • CONTRIBUTING.md: records the two new modules in its source-tree list, which mirrors AGENTS.md and had been updated only there.
  • docs/configuration/vimrc.md: the guicursor example was annotated "bundled fork mode only", which stopped being true once per-mode cursor shapes started following the backend's mode.
  • KNOWN_LIMITATIONS.md: the Lua TextYankPost autocmd limitation now records that it also does not fire while the Neovim backend owns keys — the event comes from the bundled engine, which is stood down — and that the yank highlight feature is unaffected, being driven by a Neovim notification instead. Also adds the two RPC behaviours verified by hand rather than in CI, with the reason and the check for each.
  • AGENTS.md: records src/rpc/config-export.ts, src/vim/external-mode.ts, and the three new RPC specs, which the source-tree and test-organisation sections had not been updated for.
  • docs/configuration/settings.md: adds the Set up Neovim and Regenerate automatically rows, and points the Neovim backend note at the RPC-mode feature table.
  • docs/features/quality-of-life.md, docs/features/tables.md: corrects two qualifications that predate the Neovim backend. Yank highlight was documented as requiring the bundled fork engine, which is now misleading — it works under the backend too, where the fork is stood down and a TextYankPost notification drives the same renderer. Table-nav's "requires the bundled vim engine" was accurate about built-in vim but read as excluding the backend, under which it is measured working.
  • docs/features/neovim-backend.md: removes a table left over from before the configuration export existed, which duplicated four rows of the generated set and listed snippets beside them — implying snippets were generated when no such block exists, and none is cheap, because the bundled snippets are vault-stored JSON and Lua-DSL definitions rather than a setup() call. Adds a warning that the generated file is a starting point rather than parity: dial.nvim's default augends do not cover hex colours, booleans, dates or checkboxes, and nvim-surround's defaults omit dsf/csf and insert-mode <C-G>s.
  • README.md: the third-party Lua compatibility paragraph is scoped to the bundled fengari runtime. It described the shim while reading as a statement about the plugin, which understated the Neovim backend in particular — that runs the user's own Neovim, where LuaJIT FFI is available and flash.nvim works.

Full Changelog: 0.150.0...1.0.0

Don't miss a new motions release

NewReleases is sending notifications on new releases.