2026-07-21 Neovim Claude Integration Fixes
What I set out to do
Work through a backlog of six friction points in the claudecode.nvim integration in the dotfiles: a recurring vim/fs:837: assertion failed! error from render-markdown, auto-restore sometimes resuming the wrong Claude session, tmux paste (prefix+v) only reaching nvim-hosted Claude panes, highlighted-but-uncopied tmux selections not being sent, the Snacks dashboard at startup, and the open-file hook putting buffers “on top of” the Claude terminal.
What I actually did
Systematic root-cause pass before any fixes, then six atomic commits:
- Dangling cwd heals itself (
09b2d47): the assertion wasassert(uv.cwd())insidevim.fs.abspath; nvim’s process cwd had been deleted out from under it (worktree cleanup / cwd synced from Claude). render-markdown was only the messenger. NewCwdGuardautocmds track the last good cwd and re-anchor to the nearest existing ancestor. Notable:vim.fn.getcwd()also fails once the cwd dangles, so the guard has to remember the path itself. - Session ownership clobbering fixed (
59b4220): session files are keyed by cwd+branch, so a second nvim instance in the same dir overwrote the owner record and session id (last-writer-wins) and deleted the files on close. Claiming now mirrors restore (should_claim), cleanup is gated onis_owned_by_me, and the SessionStart binder hook compares the recorded owner against its parent’s$NVIMsocket. Covered by the lua spec plus a new hermetic bash test. - Paste to bare claude panes (
af8b81b): prefix+v now falls back to adirectclaude pane, writing the same context-headed temp file and bracketed-pasting an@pathmention. Verified end-to-end on an isolatedtmux -Lserver against a fake compiledclaudebinary (macOS SIGKILLs copied platform binaries, so/bin/catcopies don’t work as fakes). - Live selection send (
a2cfb33):bind vcopies a present copy-mode selection first viacopy-selection-no-clear. - Straight into Claude at startup (
314cac8): dashboard disabled; argless start opens a fresh Claude session when nothing restores. - Open-file hook float bug (
46eba1c): the hook’s window picker accepted floating windows (snacks notifier popups), so files opened inside a float over the terminal. Now only normal windows qualify, with a top-level left vsplit created when only the terminal is visible.
Applied via hm switch; all pre-commit checks green.
What was striking
- One root cause (deleted cwd) manifesting as a plugin-specific rendering error two layers away; the fix belonged in core config, not the plugin.
- Verifying tmux scripts hermetically is genuinely possible with
tmux -L <socket>plus a fake binary, but macOS AMFI kills relocated platform binaries with SIGKILL 137, which cost a debugging loop. - A test shim that resolves itself through
$PATH(env tmuxfinding the shim namedtmux) exec-loops silently.
Related
Dotfiles repo ~/.config, commits 09b2d47..46eba1c.
Follow-ups (same session)
- Swap prompt from hook opens (
5620bc3): stale crash swapfiles made the open-file hook’s:editraise the modal E325 prompt, pausing the editor. Live-instance swaps never prompt (nvim auto-ignores with W325); only dead-owner swaps do. Fixed with a scopedshortmess+=Aaround hook opens after discoveringSwapExistsautocmds do not fire in RPC/scheduled context (E325 surfaces as a lua error instead). Manual edits keep the full recovery prompt. - Fresh-start layout (
1fe084c): dashboard-less startup left Claude in a 40% split beside an empty scratch buffer; nowwincmd omakes it fullscreen when nothing was restored.
User-verified: fullscreen fresh-start and claude paste confirmed working in real use. Still watching: wrong-session restores (needs multi-instance days to disprove) and swap-prompt suppression (needs a stale swap to occur naturally).