Skip to content

fix(selection): end charwise and blockwise selections on a whole character - #367

Merged
ThomasK33 merged 19 commits into
mainfrom
fix/selection-multibyte-utf8
Nov 1, 2026
Merged

ThomasK33 merged 19 commits into
mainfrom
fix/selection-multibyte-utf8

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

Why

The goal is to clear the open follow-up issues (#362 now, #350, #326 and #335 earlier), one small reviewed PR per bug. Part of #362. This PR handles item 1.

A charwise or blockwise visual selection that ended on a multibyte character was cut at that character's first byte. The '> mark, the v mark and the cursor all point at the first byte of a character, and selection.lua used that byte as the last byte of the selection. selection_changed then carried invalid UTF-8, and its end column pointed inside the character.

  • A strict WebSocket client drops the connection. The harness client (Node 26 ws) closed with code 1006 at the issue's repro: the line naïve café, then 3G 0 v w e.
  • The real Claude Code CLI 2.1.312 stays connected, but it shows no selection.

The end column now comes from vim.fn.matchend(line, "\_.", col - 1), the end of the character that starts at the end position. That is the character Neovim itself selects and yanks there, with its composing characters, and on Neovim 0.11+ with the rest of its grapheme cluster.

  • An earlier version of this PR used matchstr with a pattern anchored at the column (for example \%13c.), and that was not enough on Neovim 0.11+. There, e and $ can leave the cursor on the inner consonant of a Devanagari conjunct (the त of स्ते), while a regex scan from the line start treats स्ते as one character and finds nothing at that column.
  • With selection=exclusive, a selection of more than one position ends after the character before the end position. On 0.11+ that character can be a cluster that reaches past the end position (स्ते before its त), as Neovim's own yank shows.
  • A line with a NUL byte goes to matchend with NUL as NL, because vim.fn would pass the Lua string as a Blob and raise E976.

Linewise selections were already whole lines and do not change.

Two more inputs sent invalid UTF-8. The verifier saw both drop the harness client with code 1006 on Neovim 0.8.3, 0.10.4 and 0.12.3, and on main too:

  • A stale mark. A mark keeps its byte column when text before it on the line changes. Select cd on abcdef, press Esc, insert aé at the line start, then press gv: '< and the v position are now on the second byte of é. With a backward selection, the stale position is the end instead. Such a position now moves back to the first byte of its code point, at both ends.
  • A buffer line that is not valid UTF-8, for example a file opened with :e ++enc=utf-8 ++bad=keep that holds the byte 255. The selection text now replaces each invalid byte sequence with U+FFFD, the way a UTF-8 decoder does. The positions still count the bytes of the buffer line.

What changed

  • lua/claudecode/selection.lua:
    • selection_end_byte(line, start_coords, end_coords) gives the end column of charwise and blockwise selections. get_visual_selection (live updates) and get_visual_selection_from_marks (the flush on visual exit) use it.
    • code_point_start(line, col) moves a column inside a well-formed multibyte code point back to that code point's first byte. A continuation byte outside such a sequence is a character of its own in Neovim and keeps its column. Both functions apply it to the start column, and selection_end_byte applies it to the end column.
    • valid_utf8(text) replaces each ill-formed UTF-8 sequence with U+FFFD (pure Lua). Both functions apply it to the text they return. selection_changed, getCurrentSelection, getLatestSelection and getOpenEditors all send that text.
  • tests/selection_test.lua: a base mock of vim.fn.matchend for the two patterns the helper uses. It models composing characters, a virama that joins the next consonant on 0.11+, E976 for a NUL byte, and a byte that does not start a complete UTF-8 sequence as one character. New tests assert that the sent text is valid UTF-8 and equals the selection, plus the columns. They cover:
    • the live and flush paths
    • multi-line selections that start and end on multibyte characters
    • a composing character and a 4-byte emoji
    • a selection that ends on, or is only, a conjunct's inner consonant
    • blockwise, linewise, selection=exclusive, and a NUL byte
    • a stale start or end inside a character
    • a line that is not valid UTF-8: a byte 255, a cut-off 日, a lone continuation byte, an overlong form and an encoded surrogate
    • a selection of only a lone continuation byte
  • lua/claudecode/README.md: contract lines for the units, where a selection starts and ends, and the valid UTF-8 text.

Scope

Only where a charwise or blockwise selection ends. The units do not change. The plugin sends 0-based UTF-8 byte offsets, and PROTOCOL.md does not define the unit. VS Code positions count UTF-16 code units, so on a line with non-ASCII text the numbers differ from VS Code's. I did not check how Claude uses the columns.

The start column comes from Neovim's marks and cursor, moved back to a code point's first byte. On Neovim 0.12.3 the start can be the inner consonant of a conjunct ($v on नमस्ते). Neovim's own yank starts there too, so the plugin sends ते, the same as the yank. With a stale mark, the text differs from Neovim's yank on purpose: gv then y yanks the raw bytes (\xa9a in the repro above), and the plugin sends éa.

Known and not changed here: a selection that includes the line break (v$, or l past the last character) is sent without that line break, with end column = line length + 1. Main does the same. Blockwise selections are still sent as one contiguous span.

Blast Radius

Users who select text that ends on a non-ASCII character: accented letters, CJK, emoji, Hindi and other scripts with conjuncts or combining marks. Their selection now reaches Claude whole. Before, a strict client dropped the connection, and the real CLI showed nothing. Users who press gv after an edit, or who select text in a file that is not valid UTF-8, keep their connection. Each charwise or blockwise update now makes one matchend call. Each visual update also makes one pass over the selected bytes in Lua, which I measured at 0.3 ms for a 125,000-byte selection of accented text (item 8 below).

Users with selection=exclusive see a change on plain ASCII text too. A selection now sends one character fewer than main did, the same text Neovim yanks. Main was wrong. For example, 0vl on abcdef sent ab (end 2) on main, while Neovim yanks a. This PR sends a (end 1), on Neovim 0.8.3 and 0.12.3. In the verifier's matrix, about 530 cases per run differ from main for this reason.

Verification

  1. The verifier's grapheme matrix (its hs.lua and analyze.py): 65 cases on mb2.txt (accents, CJK, emoji, नमस्ते, ZWJ 👩‍💻, flags 🇩🇪🇫🇷, skin tone 👍🏽, नमस्ते दुनिया). It runs real keys, live and on the flush, inclusive and exclusive, on Neovim 0.8.3, 0.9.5, 0.10.4 and 0.12.3, so 130 sends per row.

    Code Invalid UTF-8 sends Pass (inclusive / exclusive)
    main 879828a 70 to 80 of 130 inclusive, 64 to 72 exclusive 36 to 38 / 6 of 130
    this PR dd06671 0 of 1040 0.8.3 to 0.10.4: 118 / 130 of 130. 0.12.3: 108 / 128 of 130

    All 60 remaining fails on this PR fall in two groups. 56 are the line-break case above, where the text and the yank match except for the line break. 4 are 7G$v on 0.12.3, where the text equals Neovim's yank (ते) but the analyzer's cluster split puts the start inside स्ते.

  2. verify-claudecode-nvim (agent-tty, real Neovim 0.12.3, ws-client.mjs as Claude), with a typed buffer of नमस्ते, ZWJ, flag, skin-tone and नमस्ते दुनिया lines.

    • main code: 1G0ve drops the client, and ws-client.log shows closed (code 1006).
    • this PR: the client stays alive through 8 checks. 1G0ve and the flush send नमस्ते (end 18). 1G$v sends ते, and 1G$v0 sends नमस्ते. The ZWJ, flag and skin-tone selections all arrive whole.
    • The earlier run with naïve café, CJK and emoji also passes.
  3. My headless matrix on Neovim 0.8.3, 0.9.5, 0.10.4, 0.11.6, 0.12.3 and 0.12.6: 28 of 28 checks pass on every version (main: 2 of 28). It compares each sent text with Neovim's gvy yank, inclusive and exclusive.

  4. A NUL byte: a^@b, then 0vl, sends a\0 (end 2) inclusive and a exclusive on 0.8.3 and 0.12.3, without an error.

  5. Real Claude Code CLI 2.1.312 (earlier head c9aff94, same inclusive code path for this line): main sends 11 bytes and the CLI shows no selection. This PR sends 12 bytes, and the CLI shows 1 line selected.

  6. Unit tests: each test commit fails against the code before it, and the fix commit after it makes the test pass.

    • 1b29be7: 6 of its 7 tests fail. Fixed by c9aff94.
    • 3b6853e: 2 of 3 fail. Fixed by 8cc3080.
    • 2847498: 2 of 2 fail. Fixed by 3eccc94.
    • 40bdfd0: 1 of 1 fails. Fixed by c786c9e.
    • 81073ad: 1 of 1 fails. Fixed by 82d5197.
    • 17da7fa: 5 of 5 fail. Fixed by 2ed1fc6.
    • d8ede18: 1 of 1 fails. Fixed by 445f73f.

    mise run check (0 warnings), mise run test (830 successes, 0 failures), stylua --check, and prettier --check pass on the head commit.

  7. The verifier's break-it probes (its hb.lua, plus four probes I added: a backward selection whose stale anchor falls on the middle byte of 日, a forward one whose stale start does, and two selections that start on the lone 0x80 of a<0x80>b). Real keys, live and on the flush, on Neovim 0.8.3 and 0.12.3, inclusive and exclusive, 44 records per run.

    Code Invalid UTF-8 sends (inclusive / exclusive), on both versions
    before, a0c16cf 15 / 7: the stale-mark probes and the lines with byte 255 or a lone 0x80
    this PR 445f73f 0 / 0

    After the fix, the aé repro sends éa (start 1, end 4), the backward stale case sends x日 (end 4), abc<255>def sends abc U+FFFD def (end 7), and the lone 0x80 alone sends U+FFFD (start 1, end 2).

  8. Cost of the UTF-8 pass, timed on the function itself, 50 runs, Neovim 0.8.3 and 0.12.3: 0.14 ms for 125,000 ASCII bytes, 0.27 ms for 125,000 bytes of accented text, and about 2 ms for 125,000 bytes where every fifth byte is 255. The long-line timings of get_visual_selection in hb.lua stay within run-to-run noise.

  9. verify-claudecode-nvim, round 4 (agent-tty, real Neovim 0.12.3, ws-client.mjs as Claude): the aé stale gv, the backward stale gv with x日, and :e ++enc=utf-8 ++bad=keep on a file with abc<255>def.

    • before (a0c16cf): the first stale gv drops the client, and ws-client.log shows closed (code 1006).
    • this PR: all 6 sends are valid UTF-8, and doctor reports the client alive after each one. getCurrentSelection returns abc U+FFFD def with end 7.

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

…ters

A charwise or blockwise selection that ends on a multibyte character
was cut at that character's first byte. selection_changed then carried
invalid UTF-8, and Claude's WebSocket client dropped the connection.
The new tests assert that the sent text is valid UTF-8 and equals the
selection, including composing characters, for the live and the
visual-exit paths. The six charwise and blockwise tests fail until the
fix in the next commit.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
The fix in the next commit reads the end character of every charwise
and blockwise selection with vim.fn.matchstr, so the existing selection
tests need the mock as well.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
…acter

The '> mark, the "v" mark and the cursor point at a character's first
byte, and the selection code cut the last line there. A selection that
ended on a multibyte character then sent invalid UTF-8 in
selection_changed, with an end column inside that character. A strict
WebSocket client such as Node's ws closes the connection with 1006.
Claude Code 2.1.312 stays connected but shows no selection.

The end column now moves to the last byte of the end character,
composing characters included, with vim.fn.matchstr. The sent text and
the end column then match the selection that Neovim yanks. Columns stay
0-based byte offsets.

Part of #362.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
…rule

Part of #362.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-01T01:42:26.061360Z 445f73f Manual request
🔒 Security Review ✅ Completed 2026-11-01T01:42:26.499388Z 445f73f Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 9742eaa30a

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9742eaa30a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

Comment thread lua/claudecode/selection.lua Resolved
…cter

With 'selection' set to exclusive, Neovim leaves out the character at
the end of a selection of more than one position. The sent selection
included that character. The two new tests for a backward live
selection and a multi-line marks selection fail until the fix in the
next commit.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
With 'selection' set to exclusive, Neovim leaves out the character at
the end of a selection of more than one position. The end column now
stops just before that character, which is also a character boundary.
A selection of one position keeps its character, as in Neovim.

Part of #362.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 8cc3080867

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 8cc3080867

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

On Neovim 0.11 and later, a regex scan from the line start treats a
Devanagari conjunct such as स्ते as one character, while `e` and `$`
leave the cursor on its inner consonant त. matchstr with \%<col>c.
then finds nothing at that column, so the selection was cut after the
consonant's first byte and selection_changed carried invalid UTF-8.
The mock now models that, and two new tests fail until the fix in the
next commit.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
…hend

On Neovim 0.11 and later, `e` and `$` can leave the cursor on the
inner consonant of a Devanagari conjunct, such as the त of स्ते. A
regex scan from the line start treats स्ते as one character, so
matchstr with \%<col>c. matched nothing there. The end column then
stayed on that consonant's first byte, and selection_changed carried
invalid UTF-8.

The end now comes from vim.fn.matchend(line, "\_.", col - 1), which
matches the character that starts at the end position. That is the
character Neovim selects and yanks there, and the match always ends on
a code point boundary. `\_.` also matches the NL that stands for a NUL
byte.

Part of #362.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
…er consonant

With 'selection' set to exclusive, Neovim ends the selection after the
character before the end position. On Neovim 0.11 and later, that
character can be a whole cluster that reaches past the end position,
such as स्ते before its त. The mock's matchend now models the scan
from the line start, and the new test fails until the fix in the next
commit.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
…ore the end

Neovim ends an exclusive selection after the character before the end
position. On Neovim 0.11 and later, that character can be a cluster
that reaches past the end position, such as स्ते before its त, and
Neovim then yanks the whole cluster. The end column now comes from a
match of the last character that starts before the end position, so it
follows Neovim on every version.

Part of #362.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
nvim_buf_get_lines returns a NUL byte as "\0", and vim.fn passes such a
Lua string as a Blob, so the matchend call raised E976 from the
selection autocmds. The mock now raises the same way, and the new test
fails until the fix in the next commit.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
nvim_buf_get_lines returns a NUL byte as "\0", and vim.fn passes such a
Lua string as a Blob, so matchend raised E976 inside the selection
autocmds. The line now goes to matchend with each NUL as NL, which is
how Neovim stores NUL in a line. Both are one byte, so the byte
offsets do not change.

Part of #362.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
… valid UTF-8

Part of #362.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: dd06671868

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: dd06671868

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…t is not valid UTF-8

A mark keeps its byte column when text before it on the line changes. After
`gv`, the start or the end of the selection can then fall inside a multibyte
character, and the selection text is not valid UTF-8. A buffer line can also
hold bytes that are not valid UTF-8 (`:e ++bad=keep`). In both cases the
client drops the connection.

The vim mock now treats a byte that does not start a complete UTF-8 sequence
as one character, as Neovim does.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
A selection position inside a multibyte code point, such as a stale mark
after an edit and `gv`, now moves back to the code point's first byte. This
applies to the start in get_visual_selection and
get_visual_selection_from_marks, and to the end in selection_end_byte.

The selection text then replaces each ill-formed UTF-8 sequence with U+FFFD,
so no buffer content can make selection_changed or the selection tools send
invalid UTF-8. Positions stay byte offsets into the buffer line.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2ed1fc6b1f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

Comment thread lua/claudecode/selection.lua Resolved
@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 2ed1fc6b1f

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

On a line that is not valid UTF-8, Neovim treats a continuation byte that
follows no lead byte as a character of its own. Selecting only that byte in
`a<0x80>b` sent `a`, because the start and the end moved back to the `a`.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
…oint

code_point_start now moves a column back only when the bytes before it start
a well-formed UTF-8 sequence that covers the column. A lone continuation byte
keeps its column, and valid_utf8 sends it as U+FFFD.

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 445f73fd35

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

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".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 445f73fd35

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Nov 1, 2026
Merged via the queue into main with commit f80aa7b Nov 1, 2026
7 checks passed
@ThomasK33
ThomasK33 deleted the fix/selection-multibyte-utf8 branch November 1, 2026 02:05
@github-actions github-actions Bot mentioned this pull request Nov 1, 2026
ThomasK33 added a commit that referenced this pull request Nov 1, 2026
Re-recorded on the merge with main (#373, #367, #372). The facts and the
model are unchanged by the merge, and check.sh passes. The only covered
file main changed outside the record is selection.lua, which cannot reach
diff state.

Part of #363

_Generated with [`xum`](https://github.com/coder/xum) • Model: `anthropic:claude-opus-5-5` • Thinking: `xhigh`_
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant