Repository navigation
test: start the runner's spec Neovims with tests/minimal_init.lua - #522
Conversation
plenary's test_file takes no options, so the {minimal_init = ...} the runner
passed was dropped: the Neovim that plenary starts for a spec had no -u, did not
run tests/minimal_init.lua, and read the developer's own init file. The runner now
sets VIMINIT, which a Neovim without -u runs instead, and drops the unused option.
A comment at PLENARY_SHA tells whoever bumps the pin to recheck plenary's own
50 s per-file timeout against the runner's FILE_TIMEOUT.
_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
|
@codex review |
|
@codex security review |
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
- The runner passes tests/minimal_init.lua to the Neovim that plenary starts through VIMINIT by an absolute path in CLAUDECODE_MINIMAL_INIT, so a path with a space or % needs no escaping and a nested Neovim started from another folder finds it. The comment says VIMINIT reaches every process a spec starts. - Each file's Neovim gets its own XDG_STATE_HOME and XDG_CONFIG_HOME in temp folders, removed afterwards. A run no longer writes ~/.local/state/nvim, and the developer's ftplugin and after files do not run. plenary stays under XDG_DATA_HOME. - Two runner spec tests fail when VIMINIT is missing or names another file than tests/minimal_init.lua of the working folder, and when the state and config folders are missing or not removed. - diff_hand_over_spec.lua asserts that the swap file step opens the file, which a dropped fnameescape in the plugin's message no longer passes with a % in TMPDIR. - The cache check comment says that only the legacy diff.open_diff writes there. - minimal_init.lua loses the VimLeavePre cleanup, which ran only when PLENARY_TEST_HARNESS was set, and nothing sets it. _Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
|
@codex review |
|
@codex security review |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
…document the runner's folders - The runner spec runs the VIMINIT command with no CLAUDECODE_MINIMAL_INIT and expects no dofile call, so dropping the `if f then` guard (dofile(nil) reads standard input) turns it red. - The runner spec expects XDG_DATA_HOME in the Neovim to equal the one the runner got, so isolating it (plenary lives there) turns it red. - scripts/README.md lists the two per-file temp folders and their removal and the four variables the runner sets for each file's Neovim. _Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
|
@codex review |
|
@codex security review |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
…d clean up on SIGTERM - The runner's own Neovim takes -i NONE. It and the Neovim plenary starts share the per-file state folder, and after a timeout both wrote ShaDa at once: the first failed with E886 in most timed-out runs. - An EXIT trap removes the running file's temp files and folders, so a runner that gets SIGTERM during a file leaves none behind. It still dies with 143. - scripts/README.md says the two Neovims of a file share the folders. - Runner spec tests pin -i NONE and the SIGTERM cleanup. _Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
The EXIT trap of the runner now kills the process group of the file that runs before it removes the file's temp folders. A Neovim that outlived the runner wrote ShaDa when its timeout stopped it and made the removed state folder again. _Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
_Generated with xum • Model: anthropic:claude-sonnet-5-5 • Thinking: xhigh_
|
@codex review |
|
@codex security review |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
Codex Review: Didn't find any major issues. Hooray! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
… it contradicts (coder#533) ## Why The goal is that every claim in the README contracts of this repository is true for the code at `main`, and that each correction names its evidence. A contract is the README of one folder. It states what the code does and cites the function that does it. The branch `docs-gtz7` corrected many claims at an old commit of `main`. `main` has moved since (peer PRs coder#445 to coder#522 rewrote several passages and changed the code). This PR merges `main` into that work, re-checks every line that it changes against the current code, and fixes the lines that the peers made stale or that the merge joined into false text. ## What changed Docs only, in seven README files. The branch has four parts. 1. The commits of `docs-gtz7` (three content commits and a merge of `main`): a refresh of the contracts against the code, a pass that fixed the drift that the refresh left, and a pass that corrected claims which runtime probes disproved. 2. Five merge commits of `main` (the second for coder#522, the third for coder#524 and coder#530, the fourth for coder#506, the fifth for coder#531 and coder#526). Where a peer PR and `docs-gtz7` both changed a passage, `main`'s text won. A `docs-gtz7` correction stayed only where it is still true. 3. Nine correction commits. Six verifier agents, one per file or half file, checked every line that this diff changes against the code. They ran headless Neovim 0.12.3 probes (one on 0.10.4) where reading did not decide the claim. I applied their corrections in commits that group the files: server, root and plugin, tools, lua, terminal, scripts, the main-text fixes, and one for the exit sentences and the last seven findings. A second verifier then found one false sentence (the exit sentence below) and seven wording findings, which that last commit fixes. 4. One commit for two P2 findings of the Codex code review on `42ef1bd1`. First, `stop` and the token mismatch in `start` try to remove the lock file and only log a warning when that fails (`lua/claudecode/init.lua:642-647`, `:605-608`), so three sentences in `README.md` now say "tries to". Second, the headless auto-setup check in `plugin/README.md` used `--cmd "set rtp^=$PWD"`, which Ex splits at a space in the checkout path. I ran it in `/…/with space/repo` on Neovim 0.12.3: it fails with `E518` and prints `0`. The `lua vim.opt.rtp:prepend(vim.fn.getcwd())` form prints `2`, and `0` without the auto-setup line, as the README says. Per file, with the number of claims checked and the result: | File | Claims checked | True | False | Incomplete | Unverified | Edits | |---|---|---|---|---|---|---| | `README.md` (Repository Blueprint) | 41 | 39 | 0 | 2 | 0 | 2 | | `plugin/README.md` | 40 | 39 | 0 | 1 | 0 | 1 | | `lua/claudecode/README.md` | 198 | 173 | 11 | 14 | 0 | 23 | | `lua/claudecode/server/README.md` | 88 | 75 | 3 | 9 | 1 | 13 | | `lua/claudecode/terminal/README.md` | 106 | 100 | 2 | 4 | 0 | 5 | | `lua/claudecode/tools/README.md` | 68 | 54 | 4 | 10 | 0 | 14 | | `scripts/README.md` | 7 | 0 | 2 | 4 | 0 | 13 | A false claim was true at the old base or never true. An incomplete claim leaves out a condition that the code has. The row for `scripts/README.md` lists the seven items of the coder#462 verifiers (one more was misleading). The 541 claims of the first six rows split into 480 true, 20 false, 40 incomplete and 1 unverified. ### Re-sync items after the peer PRs - coder#530 (invalid `provider`). `lua/claudecode/README.md` said that an invalid `provider` keeps the previous value "although the warning says Defaulting to 'native'". `main` now warns `Keeping '<provider>'` (`lua/claudecode/terminal.lua:483-488`). The sentence says what the warning names now. The defect is gone from the list of code defects below. - coder#531 (release-please runner). The `run-release-please.sh` bullet of `scripts/README.md` keeps the verified text and adds `main`'s clause that `commonmark` must be on the module path too. The runner requires it at load (`scripts/release-please-runner.js:27`). The Communique, Prettier and output code that the runner bullet cites did not change. - coder#526 (snacks tests). It changes comments and dead window option assignments in `lua/claudecode/terminal/snacks.lua`. No line of this PR depends on them. - coder#524 (visual selection and plugin edits). The two conflicts were passages about the buffer listener and the flush of a visual selection. `main`'s text won in both, and the root `Editor state` bullet keeps the verified autocmd group list with `main`'s listener wording (`lua/claudecode/selection.lua:watch_selection_buffer`, `note_visual_edit`). The selection claims that this PR changes (`selection_changed` positions, `selection.lua` internals) do not depend on the code that coder#524 changed. - coder#459 (lockfile). `README.md` no longer says that `start` leaves the lock file on disk after a token mismatch: `start` removes it and stops the server (`lua/claudecode/init.lua:603-619`). The System Verification bullet about `CLAUDE_CONFIG_DIR` and `tests/lockfile_test.lua` is gone: the test hides the caller's value and stays in a private home (`tests/lockfile_test.lua:340-355`, `:439-442`). `lockfile.update` is removed from `lua/claudecode/README.md`: the module has no such function. The `start` and `stop` bullets use the text of coder#459, and `stop` is described as an attempt to remove the lock file (`init.lua:635-670`). - coder#461 (openFile reload). The reload-error passage of `lua/claudecode/tools/README.md` is the text of `main`. The claim that the recovery read does not end Visual mode stays. A probe confirmed it in both forms: plain Visual mode, and Visual mode that CTRL-O started. The read runs through `nvim_buf_call`, so the mode stays on (`lua/claudecode/tools/open_file.lua`). - coder#462 (WebSocket shell clients removed). No README cites `scripts/lib_claude.sh` or another deleted file (a grep over the seven files finds none). ### Corrections per file <details> <summary>README.md</summary> - The list of what rejects a pending `openDiff` now includes Claude's `close_tab` and `closeAllDiffTabs` calls, which reject it through `close_diff_by_tab_name` (`lua/claudecode/diff.lua:2040-2044`, `:2105-2107`, `lua/claudecode/tools/close_tab.lua:53`, `lua/claudecode/tools/close_all_diff_tabs.lua:30`). - At exit, Neovim fires `BufUnload` for each loaded buffer before `VimLeavePre`, and the proposal buffer's `BufUnload` autocmd rejects its pending diff (`lua/claudecode/diff.lua:1279-1285`). So with one pending diff the rejection runs before the `VimLeavePre` cleanup. With two or more pending native diffs there is one exception: the reject of a new-file diff in the current tab with `diff_opts.on_new_file_reject = "keep_empty"` (the default) deletes its proposal buffer (`diff.lua:1171-1175`, `:1523-1524`), Neovim's exit loop then stops firing `BufUnload`, and the cleanup removes the other diffs with no answer. Probes on Neovim 0.8.0, 0.9.5, 0.10.4, 0.11.5 and 0.12.3 show both cases, and the `unified` layout answers every diff. The sentence states both. A fix PR (F13) follows, see "Defects in the code". - `start` removes the lock file on a token mismatch (see above). - The Processes bullet adds `:checkhealth claudecode` (`<cli> --version`, `lua/claudecode/health.lua:43-66`) and the `git rev-parse` of `terminal.git_repo_cwd` (`lua/claudecode/cwd.lua:28-42`). - `CLAUDE_CONFIG_DIR` set but empty uses `~/.claude/ide` (`lua/claudecode/lockfile.lua:11-18`). Three lines say so now. - The Side Effects bullet lists the autocmd groups of `lua/` and `plugin/` (`ClaudeCodeShutdown`, `ClaudeCodeSelection`, `ClaudeCodeMCPDiff`, `ClaudeCodeDiffCleanup`, `ClaudeCodeNativeAdopted`, `ClaudeCodeOpenFileNewFiles`, and one `snacks_win_<id>` per Snacks terminal whose split the plugin re-creates, `lua/claudecode/terminal/snacks.lua:259`), the `nvim_buf_attach` listeners (`lua/claudecode/selection.lua:175-191`) and the `"unified"` highlight groups (`lua/claudecode/diff_inline.lua:33-36`). </details> <details> <summary>plugin/README.md</summary> - Commands from auto-setup exist after a startup script that waits (`:sleep` or `vim.wait()`) lets the deferred `setup` run. They do not exist when the `-c` commands and `VimEnter` run. Headless probes on Neovim 0.12.3 (`plugin/claudecode.lua:13-17`). - The other 39 changed claims held: the load guard order, the second `source` doing nothing, the version error on Neovim older than 0.8, the failed module load shown once, the auto-setup global read twice, and the error from the deferred `setup`. </details> <details> <summary>lua/claudecode/README.md: 23 corrections with evidence</summary> - Only :ClaudeCodeSend uses the local add_paths_to_claude. :ClaudeCodeTreeAdd and :ClaudeCodeAdd call M.send_at_mention in their own code. Evidence: lua/claudecode/init.lua:703, init.lua:830, init.lua:904, init.lua:914, init.lua:974-975, init.lua:1021-1022, init.lua:1126. - expand_tilde expands only `~` and `~/...`. A path such as `~user/x` stays as it is. Evidence: lua/claudecode/init.lua:1113-1117, lua/claudecode/utils.lua:167-181. - The fallback split runs create_split, whose leave_preview_window moves from the preview window to a tiled Claude terminal window in the same tab before it splits. Evidence: lua/claudecode/diff.lua:803-810, diff.lua:263-276, diff.lua:279-281. - Since coder#483 and coder#469, diff_inline.lua no longer calls _resolve_diff_as_saved (it calls _write_proposal) and it also calls _register_reject_when_hidden and _close_proposal_window. Evidence: lua/claudecode/diff_inline.lua:471, diff_inline.lua:485, diff_inline.lua:616, lua/claudecode/diff.lua:1228-1234, commits 6eef218 (coder#483) and 36f4a0e (coder#469). - lockfile.update no longer exists: PR coder#459 (0e63cc7) removed it. Evidence: lua/claudecode/lockfile.lua:74, lockfile.lua:84, lockfile.lua:208, lockfile.lua:239, lockfile.lua:292 (the only M functions), commit 0e63cc7 (coder#459). - terminal.setup also calls get_provider, whose fallback warnings go through logger.warn, as the bullet above says. Evidence: lua/claudecode/terminal.lua:584, terminal.lua:124-130, terminal.lua:145, terminal.lua:163-166, terminal.lua:178. - A connected send that the debounce queues still fires ClaudeCodeSendComplete. Only the disconnected branch fires none. Evidence: lua/claudecode/init.lua:383-398, lua/claudecode/init.lua:1395-1400, lua/claudecode/init.lua:401-416. - The terminal call and the event happen only when _broadcast_at_mention succeeds. Evidence: lua/claudecode/init.lua:383-400. - No handshake polling exists since coder#454/coder#480. The 50 ms, 25 ms and connection_wait_delay facts repeat main's queue bullet. A send with debouncing off still queues behind a non-empty queue. Evidence: lua/claudecode/init.lua:283-289, lua/claudecode/init.lua:110-111, lua/claudecode/init.lua:1381-1396. - PR coder#452 added TextChangedI and TextChangedP. Evidence: lua/claudecode/selection.lua:387. - Since coder#452 reconcile_held_selection reports the cursor at CursorMoved, not after visual_demotion_delay_ms. A text change and another buffer also end the hold. Evidence: lua/claudecode/selection.lua:237-265, lua/claudecode/selection.lua:118-159, lua/claudecode/selection.lua:576-584, tests/selection_test.lua:2341. - find_main_editor_window skips only a fixed filetype list, not every explorer or outline sidebar. Evidence: lua/claudecode/diff.lua:157-190. - find_main_editor_window can return a window of another tab (current tab first), as the bullet above says. Evidence: lua/claudecode/diff.lua:125-142, lua/claudecode/diff_inline.lua:382-409. - Probe: proposal "a\n\n" is accepted as "a\n" (split_lines drops one trailing empty line, and the newline fix adds none back). Evidence: lua/claudecode/diff_inline.lua:44-52, lua/claudecode/diff_inline.lua:526-540, lua/claudecode/diff_inline.lua:303, probe_unified_accept.out. - An invalid diff percentage falls back to split_width_percentage, not straight to 0.30. Evidence: lua/claudecode/diff.lua:50-69. - PR coder#469 set the unified proposal buffer to bufhidden=hide (main's bullet above says so), WinClosed/BufHidden reject it. Evidence: lua/claudecode/diff_inline.lua:299, lua/claudecode/diff_inline.lua:475-485, lua/claudecode/diff.lua:1189-1222. - The close_tab tool also passes a client_id. Evidence: lua/claudecode/tools/close_tab.lua:53, lua/claudecode/diff.lua:2105-2148. - At qa! the BufUnload autocmd (diff.lua:1283) calls _resolve_diff_as_rejected before the VimLeavePre cleanup runs, for one pending diff. With two or more native diffs, a keep_empty reject of a new-file diff deletes its buffer and stops the exit loop, so the later diffs stay pending until the cleanup (the Augroups bullet states this). Evidence: lua/claudecode/diff.lua:1171-1175, lua/claudecode/diff.lua:1279-1285, lua/claudecode/diff.lua:1523-1524, probes on 0.8.0 to 0.12.3. - PR coder#478 added the WinClosed autocmd that deletes the handler. Evidence: lua/claudecode/diff.lua:455-463, tests/unit/diff_new_tab_terminal_handler_spec.lua:127. - require('claudecode').setup validates log_level (assert) before logger.setup runs. Evidence: lua/claudecode/init.lua:462-465, lua/claudecode/config.lua:15, lua/claudecode/config.lua:98-106, lua/claudecode/logger.lua:26-40. - Neovim 0.10 has no 'messagesopt' and always stops. The parenthetical read as if only 0.11+ stops. Evidence: probe_echospace.out (0.10.4: NO SUCH OPTION, 0.11.5: hit-enter,history:500). - With laststatus=1 and two windows v:echospace is 68, not 51. Evidence: probe_echospace.out. - PR coder#445 removed the skip. The loop runs every file. Evidence: scripts/run_integration_tests_individually.sh:86-88. </details> <details> <summary>lua/claudecode/server/README.md: 13 corrections with evidence</summary> - The pending check/idle pair of a tick runs judge_silence and judge_stall, not only a stall confirmation Evidence: lua/claudecode/server/tcp.lua:601-611, tcp.lua:424-435, tcp.lua:490. - The state stays `closing` after on_close. On_close only removes the client (README line on client states says it never becomes `closed`) Evidence: lua/claudecode/server/client.lua:363-371, tcp.lua:227-228, tcp.lua:275-283. - start_close_timer also sets `closed` when a keepalive window passes with no write progress Evidence: lua/claudecode/server/client.lua:459-466, client.lua:140-144. - Completes the state list: _disconnect_client does not touch client.state (probe: on_disconnect saw state `connected` after EOF) Evidence: lua/claudecode/server/tcp.lua:209-217, tcp.lua:255-294, probe-server/events.log client_8, client_9. - PR coder#480 added connected_at to get_client_info Evidence: lua/claudecode/server/client.lua:522-531, client.lua:292. - PR coder#454 moved process_mention_queue from on_connect to on_open Evidence: lua/claudecode/server/init.lua:57-73, client.lua:294-296. - After the merged sentences, `It` reads as process_data or the Close frame. Process_data does check the whole message Evidence: lua/claudecode/server/frame.lua:156-168, client.lua:184-188. - parse_frame rejects a 1-byte Close payload with 1002 (same bullet group says so), so only an empty payload reaches the 1000 default Evidence: lua/claudecode/server/frame.lua:157-160, client.lua:352-361, probe S5, S6. - Since PR coder#455/coder#488 last_pong plays no part in the keepalive Evidence: lua/claudecode/server/client.lua:381-383, client.lua:529, tcp.lua:220-222, tcp.lua:474-475. - PR coder#497/coder#500: tools/call answers -32602 for a non-string name before handle_invoke. Contradicts the JSON-RPC errors bullet Evidence: lua/claudecode/server/init.lua:353-368. - Two callbacks share the name: process_data's on_error carries client errors (other bullets say so), server.on_error only server failures Evidence: lua/claudecode/server/tcp.lua:121,190,196,524, tcp.lua:229-231, client.lua:202,275. - The 101/error write callback calls on_error when err or not success Evidence: lua/claudecode/server/client.lua:271-279, probe events client_1..3. - The message carries the tool name Evidence: lua/claudecode/tools/init.lua:90-96. </details> <details> <summary>lua/claudecode/terminal/README.md: 5 corrections with evidence</summary> - FALSE since coder#464: native close and the first get_active_bufnr of a module copy adopt a marked live terminal. The sentence also contradicts the same bullet's second sentence. Evidence: lua/claudecode/terminal/native.lua:292, lua/claudecode/terminal/native.lua:574-577, probe probe-terminal/probe_native_reload.out. - INCOMPLETE: on Neovim 0.12.3 termopen also returns 0 under :silent! for a modified buffer, which is not an invalid argument. Evidence: probe probe-terminal/probe_termopen.out (cases D, E, F, G, H, I), lua/claudecode/terminal/native.lua:237-242. - INCOMPLETE: through the dispatcher a missing Snacks never reaches the ERROR notification, because get_provider falls back to native. Evidence: lua/claudecode/terminal.lua:133-146, lua/claudecode/terminal.lua:192-196, lua/claudecode/terminal/snacks.lua:691-693. - INCOMPLETE: an unset snacks_win_opts.position falls back to split_side (left or right), so cc_show re-creates a split itself. Evidence: lua/claudecode/terminal/snacks.lua:374-377. - INCOMPLETE: for an adopted terminal each entry with a config replaces term._cc.config, so it is not the config captured at open. Evidence: lua/claudecode/terminal/snacks.lua:463-474, lua/claudecode/terminal/snacks.lua:646-647, lua/claudecode/terminal/snacks.lua:747. </details> <details> <summary>lua/claudecode/tools/README.md: 14 corrections with evidence</summary> - FALSE after PR coder#500: handle_invoke uses `params.arguments or {}`, so no Lua error and no -32000 (probe p1: openFile/saveDocument -32602, getDiagnostics []). Also contradicted line 17. Evidence: lua/claudecode/tools/init.lua:88, lua/claudecode/server/init.lua:357, probes/p1_static.out. - FALSE: the script loops over every tests/integration/*_spec.lua with no skip. PR coder#445 (a4d3238) made it run command_args_spec again. Evidence: scripts/run_integration_tests_individually.sh:86. - Misleading: bufnr() finds 'file.txt', 'b/file', './dir/../dir/file.txt' and '' (current buffer). Probes p1 and p5. Evidence: lua/claudecode/tools/save_document.lua:31, lua/claudecode/tools/check_document_dirty.lua:27, probes/p1_static.out, probes/p5_bufnr.out. - INCOMPLETE after PR coder#457: with no stored selection it reads the editor (the bullet above says so), so it does not always return the stored selection. Evidence: lua/claudecode/tools/get_current_selection.lua:37-39, :83-95. - INCOMPLETE: the -32603 table is returned, not raised, so build_response sends it as `{ result = ... }`. Also joins the hard-wrapped bullet like the rest of the file. Evidence: lua/claudecode/tools/close_tab.lua:26-33, lua/claudecode/tools/init.lua:180-182, lua/claudecode/server/init.lua:387. - Format: the merge wrapped a quoted error message across two lines inside a code span, so a search for the message misses it. Every other bullet is one line. Evidence: lua/claudecode/tools/open_diff.lua:56. - Coherence: the inserted sentence split 'finds it' from 'Without one', so 'one' no longer names the loaded buffer. The moved claim is TRUE (probe p1). Evidence: lua/claudecode/tools/open_file.lua:133-140, probes/p1_static.out. - INCOMPLETE: expand_tilde leaves `~user/...` unchanged. Evidence: lua/claudecode/utils.lua:167-181, lua/claudecode/tools/open_file.lua:547. - INCOMPLETE: with makeFrontmost=false the answer is the JSON object, and in the middle of a command openFile appends a suffix. Evidence: lua/claudecode/tools/open_file.lua:735, :741, :746-760, probes/p4_openfile_save.out. - INCOMPLETE: source and code are nil (absent from the JSON) when the diagnostic has none. Evidence: lua/claudecode/tools/get_diagnostics.lua:82-83. - INCOMPLETE: nvim_get_current_tabpage() returns a handle. Probe p1: tabpagenr() 2, handle 3, groupIndex/viewColumn 2/3. Evidence: lua/claudecode/tools/get_open_editors.lua:19, :65-66, probes/p1_static.out. - INCOMPLETE: reload_file_buffers skips a modified buffer, and a diff kept for another client does not close. Evidence: lua/claudecode/diff.lua:2015-2018, :2025-2027, :1104. - INCOMPLETE: no API tells which loaded buffers were opened before their file existed. The loop marks every loaded, named, normal buffer whose file is not readable. Evidence: lua/claudecode/tools/open_file.lua:170-179. - INCOMPLETE (minor): with diff_opts.layout = "unified" diff.lua hands the result to diff_inline, which builds it. Evidence: lua/claudecode/diff.lua:1050-1053, lua/claudecode/diff_inline.lua:526-537. </details> <details> <summary>scripts/README.md: 7 items of the coder#462 verifiers, each checked against the scripts</summary> - The CI paragraph named no job for "the same job". It now names the three jobs of `.github/workflows/test.yml` (`unit-tests`, `integration-tests`, `covered-lock`), says that `integration-tests` runs the runner twice (the second time with a `TMPDIR` that holds a space and a `%`), and says that `Required` needs only `unit-tests` and `integration-tests` (`test.yml` lines 52-80, 98-167, 215-234). - `tla/check-lib.sh` exit precedence: 2 wins over 1, and 1 wins over 3. A stale lock plus a broken rule exits 1. `--hash-only` exits 0 or 3, and `--record` exits 0. A script that sources the lib and ends without `tla_main` turns an exit 0 into exit 2. Probed with a fake `curl` and a fake TLC: default with a broken rule and a stale lock gave rc=1, default with a pass and a stale lock rc=3, a broken `TypeOK` rc=2, `--record` on a stale lock rc=0 (`scripts/tla/check-lib.sh:81-84`, `:226-246`, `:313-333`, `:406-419`). - `tla_init` comes right after `SKILL` and `MODEL`, before `covered_block` (`.agents/skills/verify-diff-lifecycle/check.sh:15-29`, `scripts/tla/check-lib.sh:461`). - `run-release-please.sh` prints nothing itself, and exits 1 with no message when `release-please` is missing. The runner's stdout and file outputs are listed: release-please's own log lines go to stdout (info and debug) and stderr (warnings and errors), the `key=value` lines go to `GITHUB_OUTPUT` or to stdout when it is unset, and `--dry-run` prints JSON after the log lines and still runs Communique. Probed without a token and without network: wrapper rc=1 with no output, and `Error: GITHUB_TOKEN must be set` (`scripts/release-please-runner.js:487-492`, `:555`, `:590`, `:614`). - `--record` and `--hash-only` exit before the TLC jar is fetched, so they never download it and need no `java`. A plain run and `--mutants` do. Probe with an empty cache and a fake `curl`: `--hash-only` and `--record` made 0 `curl` calls, a plain run and `--mutants` made 1 and exited 2 (`scripts/tla/check-lib.sh:452-453`, `:147-151`). - The Lua repros: under `nvim -l` an uncaught error prints `E5113` and exits 1, `cquit N` and `os.exit(N)` exit N, and a bare file name run from `scripts/` fails with `module 'claudecode.diff' not found`. In the `:luafile` form of CI an uncaught error falls through to `cquit 2` (probes on Neovim 0.12.3, and `-l` exit 1 on 0.9.5, 0.10.4 and 0.11.5). - `repro_issue_70.sh` and `repro_issue_277.sh` side effects that the file did not list: work dir names, the `att-home` folder, the empty `/tmp/agent-tty/<8 hex>` socket folder and the `homes.json` entry that `agent-tty` leaves, the inherited environment, the three background probes of 70 and their `PORT.lock` files (a SIGKILL leaves the lock), the unpinned diffview.nvim clone of 277, the state and cache files of `NVIM_APPNAME=issue-277`, the `perl` timeouts, and the nested copy under an existing `issue277-artifacts-latest`. A full run of 277 with private dirs took 32 s. A SIGKILL run left the lock file and 3 processes until `agent-tty destroy`. - `manual_test_helper.lua` calls the handler itself, so no response sender is stored for its coroutine. Any resolution after the handler yields (accept, reject, or `qa!`) logs `No global response sender found for coroutine`. It is expected and harmless (all four CI-form runs exited 0). </details> ## Scope - No code changes. `covered.lock` does not change. - Lines of `scripts/README.md` about `repro_issue_218.sh` stay as they are on `main`. PR coder#458 owns them, and its merge will change them. - I checked every line that this diff changes. Lines that the diff does not touch were not re-verified, except where a verifier found one false while reading its neighbors. Those fixes are in the last correction commit: `server/mock.lua` is required by `tests/unit/server/mock_spec.lua`, the augroup and `utils.lua` lists, the empty `CLAUDE_CONFIG_DIR`, and the Processes bullet. - Probes ran on Linux with Neovim 0.12.3. One probe ran on 0.10.4. The old Neovim claims that earlier lanes proved on 0.8 were not run again. - One claim stays unverified: the sentence in `lua/claudecode/server/README.md` that a client whose EOF the server read in the same read as its request is handled as the code says. Six probe tries did not produce that case. The sentence is a softening, so I kept it. - The terminal README says that the wait before a `close` does not prevent a hang at exit. A probe saw the hang with and without a `close`, so the probe does not prove that the buffer delete causes it. I did not open neovim/neovim#42508 (the host has no network). ### Defects in the code that the verifiers found These need a code fix and a test, so they are not in this docs PR. 1. Accepting a `unified` diff drops one trailing empty line of the proposal: `"a\n\n"` is accepted as `"a\n"` (`lua/claudecode/diff_inline.lua:44-52` and `:531`). The native layout is not affected. A probe shows it. The README says the accepted text is the proposal lines that setup computed. 2. `lua/claudecode/server/tcp.lua:19` says the PRNG is seeded once per process. It is seeded once per load of the module. 3. `lua/claudecode/server/init.lua:59-60` logs "Authenticated WebSocket client connected" from `on_connect`, before the authentication check runs. 4. `lua/claudecode/cwd.lua:36-42` runs the same `git rev-parse` twice. 5. At exit, a pending native diff can stay unanswered. Two native diffs are pending, and the first one (the lower proposal buffer number) is a new file in the current tab with `diff_opts.on_new_file_reject = "keep_empty"` (the default). On `:qa!` its reject deletes its proposal buffer (`lua/claudecode/diff.lua:1171-1175`, `:1523-1524`), Neovim's exit loop stops firing `BufUnload` (`src/nvim/main.c`, the `bufref_valid` check), and the `VimLeavePre` cleanup removes the second diff with no answer. Reproduced on Neovim 0.8.0, 0.9.5, 0.10.4, 0.11.5 and 0.12.3, with and without the server. A separate PR (F13) will make exit answer every pending diff. It will change the exit sentences of this PR: the openDiff bullet in `README.md` (Global Invariants, the sentence that starts "When Neovim exits") and the Augroups bullet in `lua/claudecode/README.md` (the sentences about `BufUnload` and the exception). ## Blast Radius Seven README files. Nothing loads them at runtime. A wrong correction here misleads a reader but breaks no behavior. ## Verification - Six Opus verifiers, one per file (two for the 198 claims of `lua/claudecode/README.md`). Each checked every claim that the diff adds or changes against the code, and every `[file:symbol]` citation, with file and line evidence. Their findings, edits and probe scripts are in the evidence folder of the run. - A second, independent verifier checked the head `bcacd14c` (134 sentences and all seven scripts items, with probes on Neovim 0.8.0 to 0.12.3). It found one false sentence (the exit sentence, now fixed with its exception) and seven wording findings (fixed). It found no other false claim. - Citation check on the final head: 760 citations, links and test paths over the seven files, 1 flagged. The flagged one is `[plugin/claudecode.lua:pcall(require, "claudecode")]`, an expression that `main` already cites in that README. - `mise run all`: exit 0 on the final head `e3e48af1` (1682 successes, 0 failures, 0 errors, 24 pending, `luacheck` 0 warnings and 0 errors in 178 files). `mise run format-check`: exit 0 on the final head (355 files, 0 changed). 1676 successes, 0 failures, 0 errors, 24 pending. `luacheck` reports 0 warnings and 0 errors in 178 files. The format step changes 0 files. - `git merge-tree --write-tree` of the head and `origin/main` (`3a0bb769`) is clean. - The merges of coder#524 and coder#530 changed one sentence of `lua/claudecode/README.md` and merged two conflict hunks. I checked that sentence and the merged bullets against `lua/claudecode/terminal.lua` and `lua/claudecode/selection.lua` at `a41254a4`. I did not run a verifier agent over the merge delta. --- _Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-sonnet-5-5` • Thinking: `xhigh`_
What a developer sees
The integration runner passes
{minimal_init = 'tests/minimal_init.lua'}to plenary, but plenary'stest_harness.test_file(path)takes no options and drops them. The Neovim that plenary starts for each spec has no-u, so:tests/minimal_init.lua: no leader key,timeoutlen1000 instead of 300,updatetime4000 instead of 250,ftpluginandafterfiles, so a local run depends on~/.config/nvim,~/.local/state/nvim.The only part of
minimal_init.luathat reached the spec's Neovim was the stubclaudeonPATH, which it inherited from the runner's own Neovim.What changes
VIMINIT.scripts/run_integration_tests_individually.shsetsVIMINITfor the Neovim it starts. A Neovim without-uruns$VIMINITinstead of the user's init file, so the Neovim that plenary starts now runstests/minimal_init.lua.VIMINITreads the file fromCLAUDECODE_MINIMAL_INIT, an absolute path ($PWD/tests/minimal_init.lua), so a path with a space or%needs no escaping and a nested Neovim started from another folder finds the file. A comment says thatVIMINITand the variable reach every process a spec starts. The runner's own Neovim still has-u, and the unused{minimal_init = ...}argument is gone.XDG_STATE_HOMEand oneXDG_CONFIG_HOME, in temp folders the runner removes afterwards. The Neovim the runner starts and the Neovim that plenary starts for the spec share them, so the runner's own Neovim takes-i NONE: two Neovims that write ShaDa into one new folder made the first fail withE886after a timeout. A run no longer writes~/.local/state/nvim, and theftpluginandafterfiles under~/.config/nvimdo not run. Files in the data folder (~/.local/share/nvim/site/...) still run, as onmain.XDG_DATA_HOMEstays, because plenary lives there.EXITtrap kills the process group of the file that runs, then removes its temp files and folders. Bash runs the trap when the runner gets SIGTERM too, just before the runner dies with 143 as it did before. Onmain, a runner that got SIGTERM left its temp files and left the file's Neovim running until its timeout. Killing the group first matters: a Neovim that ends later writes ShaDa and makes the removed state folder again. Two known limits: a SIGTERM that arrives beforetimeouthas created the file's process group misses the kill (the file's Neovim then runs until its timeout, as onmain), and on hosts whosetimeoutis uutils (this repo's CI image included) the SIGTERM test cannot tell a group kill from a kill of thetimeoutprocess alone. A follow-up PR fixes the test and the README wording.tests/minimal_init.lualoses itsVimLeavePrecleanup (_G.claudecode_test_cleanup). It ran only whenPLENARY_TEST_HARNESSwas set, and nothing sets it (not this repo and not plenary at CI's pin). I deleted it instead of arming it: it never ran, the plugin stops its own server in itsVimLeavePrehandler, and arming it would add an exit-time path to every spec Neovim, where a hung handler turns into aTIMEOUT:.tests/unit/integration_runner_spec.luagets six tests. The Neovim that plenary starts runstests/minimal_init.luaof the working folder by an absolute path (the test runs theVIMINITcommand withdofilestood in for). TheVIMINITcommand loads no file whenCLAUDECODE_MINIMAL_INITis missing (without itsif f thenguard,dofile(nil)would read standard input). Each file gets state and config folders that are removed afterwards.XDG_DATA_HOMEis the one the runner got. The runner's own Neovim takes-i NONE. A runner that gets SIGTERM during a file stops that file's Neovim, leaves no temp file or folder, and exits 143.scripts/README.mdlists the two per-file temp folders (shared by the file's two Neovims), their removal including on SIGTERM, and the four variables the runner sets for each file's Neovim (VIMINIT,CLAUDECODE_MINIMAL_INIT,XDG_STATE_HOME,XDG_CONFIG_HOME).PLENARY_SHAin.github/workflows/test.ymltells whoever bumps the pin to recheck plenary's own 50 s per-file timeout against the runner'sFILE_TIMEOUT..github/workflows/test.ymlnow says which code writes understdpath("cache"): only the legacydiff.open_diff, whichdiff_legacy_swap_spec.luapoints at a temp folder.tests/integration/diff_hand_over_spec.luaasserts that the:splitstep opened the expected file.Why
VIMINITand nottest_directory(<file>, opts)test_directoryor a directplenary.bustedcall would carry the options, but they change the texttest_file('<file>'in the runner's command line, which the stubnvimof the runner spec reads the file name from.VIMINITkeeps the command line as it is, and it also stops the developer's init file from running, which options alone would not. Its cost is thatVIMINITreaches nested Neovims too (the comment says so).One behavior change
The spec Neovims of
command_args_spec.luaandmcp_tools_spec.luanow run thesetup({ auto_start = false, log_level = "trace" })oftests/minimal_init.luabefore their tests, because their command line matches itsshould_loadcheck. That adds debug log lines. The results are identical.The 50 s timeout
plenary applies its own per-file timeout of 50 s (
timeout = 50000intest_harness.lua), andtest_filehas no way to change it. The runner'sFILE_TIMEOUT(40 s) plus its 5 s kill grace stays under it, as #515 set.Evidence
The runner's real script on a tree whose only spec prints the state of its own Neovim, with a fake developer init file and a fake developer
ftplugin/lua.luainXDG_CONFIG_HOME:mapleadertimeoutlenupdatetime$HOMEmain" "The whole integration runner, Neovim 0.12.3 and v0.8.0,
mainagainst this branch: 316 passed (237 rows on v0.8.0, 229 passed and 8 pending), 0 failed on both. The sortedSuccess,FailandPendingrows are identical for both trees on both versions, and so are the success counts of all 24 files.The same runner with
HOMEset to an empty folder:HOMEmain, Neovim 0.12.3.local/state/nvim/withlsp.log,nvim.log,shada,swapmain, Neovim v0.8.0.local/state/nvim/withlog,shada,swapThe two new runner spec tests, with each mutant of the script (a mutant is red when its row says "red"):
VIMINITtestVIMINITrenamed so it is not settests/minimal_init.luamainXDG_STATE_HOMEandXDG_CONFIG_HOMEnot setTwo more mutants, for the two new assertions:
if f thenguard droppedXDG_DATA_HOMEalso set to a temp folderXDG_DATA_HOMEunchangedMutants for the two tests of this round (green on the head):
-i NONEtest-i NONEremovedEXITtrap (the trap onHUP)mainNested Neovim without
-ufrom another folder (the verifier's finding): with the relativeluafile tests/minimal_init.luaof the first version, both Neovim versions printE5112 ... cannot open tests/minimal_init.luaand run with defaults. With the absolute path in the variable, neither prints an error andmapleaderis" ". A root folder namedodd dir 50%works too.The swap file step assertion in
diff_hand_over_spec.lua, underTMPDIRset toodd tmp 50%:fnameescaperemovedfnameescaperemovedmainWithout the odd
TMPDIR, the mutant with the assertion is green too: the escape only matters for a%or a space.Hang probe from #515 (a spec that sleeps 120 s), three runs on this branch: each exits 1, prints
TIMEOUT: hang_spec.lua was stopped (exit 124): nvim did not exit within 40s, and leaves no Neovim running. TheTIMEOUT:line came after 40.4 s, 44.4 s and 40.5 s. The 44.4 s run was on a loaded host (the Opus verifier saw 41.8 s to 43.7 s at a load of 37 to 40, and 41.0 s to 42.2 s onmain), so the limit plus the 5 s kill grace (45 s) is not far from a loaded run, and still under plenary's 50 s. The runner's state and config folders are gone from the run'sTMPDIR. Only the temp folder of the killed Neovim itself is left, as before.E886after a timeout, the hang probe on the head of the previous round and on this branch, three runs each:E886line6ed9c83)SIGTERM sent to the runner while the hang spec runs (the runner's
TMPDIRis a fresh folder):TMPDIRright aftermainThe trap without the process group kill left a recreated state folder in
TMPDIRafter the hang Neovim ended.Gate:
mise run clean && mise run allexit 0 (seed 1794435649, 1661 passed, 0 failed, 22 pending).actionlintandshellcheckpass. The whole runner on Neovim 0.12.3 gives 316 passed, 0 failed, with rows identical tomainat the branch base. Merged with currentmain(which adds the 3 rows of #529's spec), it gives 319 passed on 0.12.3 and 232 passed plus 8 pending on v0.8.0, with rows identical tomain.Generated with
xum• Model:anthropic:claude-sonnet-5-5• Thinking:xhigh