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 was assert(uv.cwd()) inside vim.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. New CwdGuard autocmds 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 on is_owned_by_me, and the SessionStart binder hook compares the recorded owner against its parent’s $NVIM socket. Covered by the lua spec plus a new hermetic bash test.
  • Paste to bare claude panes (af8b81b): prefix+v now falls back to a direct claude pane, writing the same context-headed temp file and bracketed-pasting an @path mention. Verified end-to-end on an isolated tmux -L server against a fake compiled claude binary (macOS SIGKILLs copied platform binaries, so /bin/cat copies don’t work as fakes).
  • Live selection send (a2cfb33): bind v copies a present copy-mode selection first via copy-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 tmux finding the shim named tmux) exec-loops silently.

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 :edit raise 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 scoped shortmess+=A around hook opens after discovering SwapExists autocmds 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; now wincmd o makes 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).