Skip to content

prevent-unusual-unicode follow-ups from #330: an East Asian lookalike, titles through gh api, glab mr merge, and the forge-composed squash message #331

Description

@HackingGate

Goal

Close the gaps the re-review and merge of #330 left in prevent-unusual-unicode: one lookalike that #330 stopped refusing, two shimmed paths whose title text reaches the forge judged as prose or not judged at all, one forge-composed commit message that reaches main unread, and two open questions -- one from #330 and one carried over unimplemented from #328's Open list.

Today

This body spells every special character as a codepoint, because the installed binary behind the gh shim may predate #330 and refuse the signs themselves.

Read at 99e23fc (main, after #330). Findings from the #330 re-review and merge.

  • A. LOW, regression from prevent-unusual-unicode refuses confusables and invisibles, not every sign that belongs to no script #330. U+3007 IDEOGRAPHIC NUMBER ZERO inside a Latin word -- g + U+3007 + od, C + U+3007 + DE -- is no longer refused. U+3007 is Han, alphabetic, and its UTS A parse that failed is not a check that passed #39 skeleton is Latin O. lookalikes (src/guard/unicode.rs:267-294) closes a word wherever an East Asian letter meets a non-East-Asian one (the crosses split, :277-283), so the word becomes g, U+3007, od: three single-script words, none of them mixed, and lookalikes_in_word (:379) never sees the pair. It was refused at 027ad48. It is the only alphabetic East Asian character whose skeleton is Latin, so it is the only one the split hides; the doc comment (:260-266) argues the split for API run into a kanji compound, which stays right.
  • B. LOW, not a regression. gh api fields are always subject kind "text" (collect_api, src/shim.rs:1582-1612, the push at :1593-1596), so gh api -X PATCH repos/o/r/pulls/N -f title=... and gh api -X PUT repos/o/r/pulls/N/merge -f commit_title=... are judged as prose: invisibles only, and a lookalike in the title passes. The same title through gh pr edit --title or gh pr merge --subject is kind "title" (:1344) and gets the confusable check. docs/REFERENCE.md:1465-1482 names what is a subject and what is prose, and says nothing of gh api.
  • C. LOW, pre-existing. The glab table (policy/principles.toml:544-559) matches mr:create, mr:update, mr:note, issue:* and api:* (:549-552), not mr:merge. So glab mr merge --squash-message / -m reaches no text guard at all, invisibles included. The gh table carries a pr:merge verb for exactly this (:530-542, with the comment that its --subject used to go into the base branch unread).
  • D. LOW, intended. subject_lines (src/guard/message.rs:215-232) takes a first line that opens with the comment character as a candidate subject, and the first line that does not as a second one (documented at :204-214 and docs/REFERENCE.md:1465-1472). So a commit.template that opens with a non-ASCII comment line is refused under ascii-only-commit-subject -- verified with a Japanese comment line -- although the editor's cleanup would strip it. That is the stated trade (never let a subject through for looking like a comment), but it lands on a template the author did not write per commit.
  • E. Observed at merge. gh pr merge 330 --squash with no body printed the shim's notice (src/shim.rs:3787-3798, the EditorPath::Forge arm): "no body was given and pr merge opens no editor here, so the message the forge composes for it was not checked." The squash commit message GitHub composes from the pull-request title and the commit list reaches main unread. The notice is honest; the commit it describes is still on main. The comment at :3787-3790 argues that the inputs were already read (title at pr create/pr edit, commits at commit-msg), which holds for the title but not for commits pushed by someone without the hook, and not for the forge's own joining text.
  • F. Carried over from prevent-unusual-unicode refuses confusables and invisibles, not every sign that belongs to no script #328's Open list, unimplemented. gh issue edit and gh pr edit are still judged on the whole resubmitted body, not on the text the edit adds. prevent-unusual-unicode refuses confusables and invisibles, not every sign that belongs to no script #330 removed the scope failure for lookalikes and signs (prose gets the invisible check alone), but an old body that already carries an invisible character still cannot be edited without UPHOLD_ALLOW.

Decision

Pending. Proposed:

  1. A. In lookalikes, do not split at a character that is drawn_as (src/guard/unicode.rs:333) the current word's script: an East Asian letter whose skeleton is Latin stays in the Latin word it sits in, and lookalikes_in_word judges it. The alternative is to document U+3007 beside U+FF43 in docs/REFERENCE.md:1454-1462 as out of scope; that is the weaker choice, because U+FF43 is out of scope for being Latin and U+3007 is a cross-script lookalike, the case the rule exists for.
  2. B. In collect_api, map a field whose key is title or commit_title to kind "title", so a pull-request title and a merge subject set through gh api get the check they get through gh pr. A field key is a closed list, not a path match. If that is judged too clever for the shim, document the gap beside the harness-hook paragraph in docs/REFERENCE.md:1465-1482 instead.
  3. C. Add an mr:merge verb to the glab table, reading --squash-message and -m as titles (a squash message's first line is the commit subject) the way the gh pr:merge verb reads --subject.
  4. D. Keep. Record it as open below rather than change it.
  5. E. Open below; no change proposed until it is ruled.
  6. F. Open below, as prevent-unusual-unicode refuses confusables and invisibles, not every sign that belongs to no script #328 left it.

Done when

  • A. src/guard/unicode.rs unit test: g + U+3007 + od and C + U+3007 + DE are refused and the report names IDEOGRAPHIC NUMBER ZERO; API run into a kanji compound still passes; a kanji compound carrying U+3007 on its own (no Latin beside it) still passes. Or, if documented instead: docs/REFERENCE.md names U+3007 beside U+FF43 and a unit test pins that it passes, so the choice is visible.
  • B. tests/shim_cli.rs: gh api -X PATCH repos/o/r/pulls/1 -f title=<Latin word with U+0430> is refused and the stub never runs; gh api -X PUT repos/o/r/pulls/1/merge -f commit_title=<same> is refused; -f body=<same> runs (prose). Or the gap is documented and a test pins the current pass.
  • C. tests/shim_cli.rs: glab mr merge 1 --squash-message <text with U+202E> is refused and the stub never runs; the same with -m; glab mr merge 1 --squash-message <Latin word with U+0430> is refused (title); glab mr merge 1 with no message runs.
  • D. src/guard/message.rs unit test pinning whichever way it is ruled: a message whose first line is # followed by a Japanese comment and whose second line is an ASCII subject is refused under ascii-only-commit-subject (kept), or passes (changed).
  • E. If the shim fetches the composed message: tests/shim_cli.rs with a forge stub that returns a composed squash message carrying U+202E refuses gh pr merge 1 --squash, and a stub that cannot be reached exits 2. If it requires --body instead: gh pr merge 1 --squash with no body is refused naming the flag to give.
  • F. tests/shim_cli.rs: gh issue edit 1 --body-file <file> over a stored body (stub for gh issue view --json body) that already carries U+200B, where the edit adds plain text, runs; the same edit adding a U+202E is refused; a forge that cannot be asked exits 2.

Open

  • D. Should a first line opening with the comment character stay a candidate subject under ascii-only-commit-subject? It refuses a non-ASCII commit.template comment that cleanup would strip; dropping it would let git commit -m "#42 ..." keep a non-ASCII subject unread. Possibly the answer differs per rule: prevent-unusual-unicode keeps both candidates, ascii-only-commit-subject (a house style, opt in) reads only the line cleanup would keep under the configured commit.cleanup.
  • E. Should the shim fetch the message the forge composes for gh pr merge --squash (or require --body/--subject) and judge it, with exit 2 when the forge cannot be asked, per the usual contract? Fetching needs the forge's composition rule, which GitHub does not expose as an endpoint; requiring the flag is the smaller mechanism and changes what gh pr merge --squash means at the shim.
  • F. (from prevent-unusual-unicode refuses confusables and invisibles, not every sign that belongs to no script #328) Should gh issue edit and gh pr edit be judged on the text the edit adds, diffed against what gh issue view --json body returns, rather than on the whole resubmitted body? A shim change, not a rule change; a forge that cannot be asked is exit 2.

https://claude.ai/code/session_01QJkWXa2HxJ6q9TMNAGSNv4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions