You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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
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.
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.
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.
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 ghpr:merge verb reads --subject.
D. Keep. Record it as open below rather than change it.
E. Open below; no change proposed until it is ruled.
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.
Short-option clusters and attached values outside gh issue edit, gh pr edit and gh pr merge were scoped out of #333 and are tracked in #336, along with a comment there recording two further low findings. The remaining U+3007 and harness-hook items are tracked in #334.
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 reachesmainunread, 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
ghshim may predate #330 and refuse the signs themselves.Read at
99e23fc(main, after #330). Findings from the #330 re-review and merge.U+3007 IDEOGRAPHIC NUMBER ZEROinside 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 LatinO.lookalikes(src/guard/unicode.rs:267-294) closes a word wherever an East Asian letter meets a non-East-Asian one (thecrossessplit,:277-283), so the word becomesg, U+3007,od: three single-script words, none of them mixed, andlookalikes_in_word(:379) never sees the pair. It was refused at027ad48. 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 forAPIrun into a kanji compound, which stays right.gh apifields are always subject kind"text"(collect_api,src/shim.rs:1582-1612, the push at:1593-1596), sogh api -X PATCH repos/o/r/pulls/N -f title=...andgh 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 throughgh pr edit --titleorgh pr merge --subjectis kind"title"(:1344) and gets the confusable check.docs/REFERENCE.md:1465-1482names what is a subject and what is prose, and says nothing ofgh api.glabtable (policy/principles.toml:544-559) matchesmr:create,mr:update,mr:note,issue:*andapi:*(:549-552), notmr:merge. Soglab mr merge --squash-message/-mreaches no text guard at all, invisibles included. Theghtable carries apr:mergeverb for exactly this (:530-542, with the comment that its--subjectused to go into the base branch unread).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-214anddocs/REFERENCE.md:1465-1472). So acommit.templatethat opens with a non-ASCII comment line is refused underascii-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.gh pr merge 330 --squashwith no body printed the shim's notice (src/shim.rs:3787-3798, theEditorPath::Forgearm): "no body was given andpr mergeopens 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 reachesmainunread. The notice is honest; the commit it describes is still onmain. The comment at:3787-3790argues that the inputs were already read (title atpr create/pr edit, commits atcommit-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.gh issue editandgh pr editare 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 withoutUPHOLD_ALLOW.Decision
Pending. Proposed:
lookalikes, do not split at a character that isdrawn_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, andlookalikes_in_wordjudges it. The alternative is to document U+3007 beside U+FF43 indocs/REFERENCE.md:1454-1462as 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.collect_api, map a field whose key istitleorcommit_titleto kind"title", so a pull-request title and a merge subject set throughgh apiget the check they get throughgh 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 indocs/REFERENCE.md:1465-1482instead.mr:mergeverb to theglabtable, reading--squash-messageand-mas titles (a squash message's first line is the commit subject) the way theghpr:mergeverb reads--subject.Done when
src/guard/unicode.rsunit test:g+ U+3007 +odandC+ U+3007 +DEare refused and the report namesIDEOGRAPHIC NUMBER ZERO;APIrun 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.mdnames U+3007 beside U+FF43 and a unit test pins that it passes, so the choice is visible.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.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 1with no message runs.src/guard/message.rsunit 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 underascii-only-commit-subject(kept), or passes (changed).tests/shim_cli.rswith a forge stub that returns a composed squash message carrying U+202E refusesgh pr merge 1 --squash, and a stub that cannot be reached exits 2. If it requires--bodyinstead:gh pr merge 1 --squashwith no body is refused naming the flag to give.tests/shim_cli.rs:gh issue edit 1 --body-file <file>over a stored body (stub forgh 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
ascii-only-commit-subject? It refuses a non-ASCIIcommit.templatecomment that cleanup would strip; dropping it would letgit commit -m "#42 ..."keep a non-ASCII subject unread. Possibly the answer differs per rule:prevent-unusual-unicodekeeps both candidates,ascii-only-commit-subject(a house style, opt in) reads only the line cleanup would keep under the configuredcommit.cleanup.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 whatgh pr merge --squashmeans at the shim.gh issue editandgh pr editbe judged on the text the edit adds, diffed against whatgh issue view --json bodyreturns, 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