Repository navigation
install.sh and openRepoShape: a $OPENREPOSHAPE_BIN_DIR the bytes refuse is read again in the locale's own charset, so a GB18030, GBK or Shift_JIS name that holds no control character and nothing drawn as another is placed, and where nothing can read it the bytes decide as before - #867
Conversation
…IR` is respelled with `cygpath -u` or refused before install.sh's fetch, and the PATH note single-quotes a directory bash would still read, and `--install` refuses one holding `:` or a newline, so install.sh names `/d/tools/bin` as the command does and the `export PATH` line does what it says (#667) #649's two leftovers, both from #656's review. install.sh read `$OPENREPOSHAPE_BIN_DIR` as typed, so with `D:\tools\bin` its `preflight:` and `done:` lines (and `install:` for an old release) said `D:\tools\bin/openRepoShape` while the command's own lines said `/d/tools/bin/openRepoShape`, and where no `cygpath` answers the command's `--install` refused (exit 2) only after install.sh had fetched it. install.sh now carries a copy of the command's rule where it reads the variable, before the symlink check and the fetch: a value that starts `[A-Za-z]:/` or `[A-Za-z]:\` is spelled with `cygpath -u --`, and is refused with exit 2 where no `cygpath` answers, fails, or answers a drive spelling again. The refusal does not echo the value. The command's PATH note printed `export PATH="$dir:$PATH"` with the directory between double quotes as it was, so `a"b$HOME c` printed a line `eval` could not parse, and a `$`, a backquote or a `\` in a name was read, not kept. `export_path_line` keeps today's line byte for byte for a directory that double quotes hold (a space and an apostrophe included) and, for one holding a `"`, `$`, backquote, `\` or `!` (bash reads that at a prompt as history), prints it between single quotes with `'\''` for an apostrophe. `--install` also refuses a directory holding a `:` or a newline, which no `$PATH` line can name, before anything is placed; the newline's refusal does not echo it. Tests feed one value through install.sh and through the command, in each spelling and each state of `cygpath`, and pin every `install:`, `preflight:` and `done:` line; the PATH note is checked as printed, by `eval` and by `bash -i`. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…holding a control character before they name it, and install.sh refuses a `:` before its fetch, so a carriage return or an escape is no longer echoed as typed (#760) #667's two checklist candidates, with rv667's NIT 2. `--install` refused a newline in `$OPENREPOSHAPE_BIN_DIR` and nothing else that is a control character: a carriage return, an escape, a tab or DEL placed the command and was printed as typed (`/tmp/x^MFORGED is not on $PATH`; an escape sequence reaches the terminal the same way). The newline arm is now `*[[:cntrl:]]*`, first in `install_commands`, ahead of the Windows spelling and the colon, and its refusal still says nothing of the value. It is `[[:cntrl:]]` and not `[![:print:]]`, so a directory with a letter the locale does not print (`café` under LC_ALL=C) is placed. Which C1 controls match is the locale's, measured with bash 5.2.21: C.UTF-8 matches U+0080 to U+009F (and U+2028, U+2029), C and POSIX match none, and the ASCII controls and DEL match in all three. install.sh learnt of a `:` or a newline only after its fetch: it fetched the launcher, ran `--install`, and said only that `--install` refused (exit 2); a release older than #667 placed the command under the directory. It now carries both arms of the command's `case`, copied as #667 copied the drive-letter one: the control-character arm first, before any line names the directory, and the colon arm after the Windows respelling, both before the symlink check and any request. Where the control-character refusal says nothing of the value, the colon refusal names the directory, as the command's does. Tests feed each value through install.sh and through the command (a colon, two directories, a drive-relative name, a newline, a carriage return, an escape, a tab, DEL, a drive spelling with a newline or an escape, a colon with an escape): exit 2, no request, nothing placed, neither stream holds a forged tail. A link in a directory holding a carriage return, a drive spelling, a colon with an escape, a letter under LC_ALL=C and LC_ALL=C.UTF-8, a C1 control under a UTF-8 locale, and the `$HOME` default pin the order and the width of the arms. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…`LC_ALL=C` and names a C1 control's two bytes, so U+0080 to U+009F is refused in every locale and whatever else the value holds, where a UTF-8 locale let one through beside a byte that is not UTF-8 (#760) Lane 1's review of #804 (finding 1): `*[[:cntrl:]]*` matches a C1 control only in a value that is valid UTF-8. One byte that is not (a lone 0xE9) makes bash match byte by byte, and then U+009B's two bytes (0xC2 0x9B) are not controls, as under `C`. Measured on 7569ffa with bash 5.2.21: under `LC_ALL=C.UTF-8` and `$OPENREPOSHAPE_BIN_DIR` set to `<dir>/caf`, 0xE9, U+009B, `31mFORGED/bin`, `openRepoShape --install` exited 0, placed the command and printed the value raw in four lines; install.sh, given the same value after a `:`, exited 2 with the colon refusal naming it, the C1 control in the line. Both files now run the check as `(LC_ALL=C; case ... *[[:cntrl:]]* | *$'\302'[$'\200'-$'\237']* ...)`: the ASCII controls and DEL byte by byte, and the two bytes of each C1 control named, so the answer is the same in every locale. Every value measured on 7569ffa and refused only under C.UTF-8, or not at all, is refused with exit 2, nothing placed, nothing echoed, and install.sh makes no request. `café` and U+00A0 are still placed. U+2028 and U+2029, which only a UTF-8 locale's `[[:cntrl:]]` matched, are now placed in every locale, and a test says so. Both comments say what the check does, and no longer say the C1 controls are the locale's. Tests: the C1 case, which skipped where no UTF-8 locale read U+009B as a control, is now five values (U+009B, the first and the last of the range, a 0xE9 byte before U+009B, and a colon beside that) in each of `C` and `C.UTF-8`, through the command and through install.sh. The placed-letter test gains U+00A0 (the byte after the range's last), U+2028 and U+2029. 18 of the 36 fail on 7569ffa's code and the other 18 are guards; 18 mutants of the two code hunks are caught. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he stacked base of #837
…holding a bidi control, a character of no width, a soft hyphen or U+2028 before any request and name it by its code point, so no line about `$PATH` shows the directory as another (#837) #804's check refuses the C0 and C1 controls and DEL, and passed every character a terminal draws as something other than itself, under `C` and `C.UTF-8` alike: the command placed itself under `<dir>/a` U+202E `forged/bin` and printed the value in four lines, and install.sh made its request. Laid out as a terminal lays out a line (fribidi), `~/` U+202E `.local/bin` reads `~/nib/lacol.`, `/opt/tools-1` U+200F `/2/bin` reads `/opt/tools-12//bin`, and `~/.local` U+200B `/bin` reads `~/.local/bin`. Both files now refuse, right after the control check, before the drive spelling, the colon, the symlink check and any request, a directory holding one of 29 code points, `MISDRAWN_CHARACTERS` in the launcher: U+00AD; the twelve bidirectional controls (U+061C, U+200E, U+200F, U+202A to U+202E, U+2066 to U+2069), as `repo_shape.spelled` escapes them (#391); U+2028 and U+2029, which end a line for every reader that keeps Unicode's line breaks, and which #804 left to a ruling; and U+200B, U+FEFF and U+2060 to U+206F, of no width, the whole block default-ignorable. U+200C and U+200D stay allowed: Persian, the Indic scripts and emoji sequences need them. So does every space. `misdrawn_characters` builds each one's UTF-8 from its code point with printf's octal escapes and looks for the bytes under `LC_ALL=C`, in the subshell #804's check uses, so one list says what is refused and what the refusal names, and no byte is read back as a number. The refusal says "holds a character a terminal draws as something other than itself (U+202E): one that is invisible, ends a line or reorders the text around it, so no line about $PATH can print the name as it reads", names every one found, and nothing else of the value. install.sh carries the list and the function as the launcher has them, and a test holds the two to one text. Tests: #804's C1 locale test in each file now runs `MISDRAWN_BIN_DIRS` too (each code point, two at once, after a byte that is not UTF-8, beside a colon, in a drive spelling), under `C` and `C.UTF-8`, through the command and install.sh, with no request; the link test takes U+202E; the lifted function names exactly its 29 over every code point that shares a first byte with them (896, on stdin, so it runs under Git Bash); `NOT_CONTROLS` drops U+2028 and U+2029 and gains a Persian word with U+200C, an emoji sequence with U+200D, U+2026 and U+FF21. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion and the redirect's location are refused unechoed for a misdrawn character too, the set takes in U+FFF9 to U+FFFB, U+1D173 to U+1D17A and U+E0001, and the refusal says a character may be drawn as another (#837) Fix round 1 on rv841's review of #841. The set (SHOULD 1). `MISDRAWN_CHARACTERS` gains FFF9-FFFB (the interlinear annotation characters), 1D173-1D17A (the musical beam, tie, slur and phrase controls) and E0001 (the deprecated language tag): format characters of no width (glibc `wcwidth` 0) that nothing needs, which the command placed and printed in four lines. The encoder builds the four-byte form too, with bash 3.2's arithmetic and `%b`'s `\0NNN`. Every other format character is left out for a reason the comment now gives: U+200C, U+200D and U+180E, which Persian, the Indic scripts, emoji and Mongolian need; the Egyptian hieroglyph and shorthand format controls, with which those scripts build their signs; the tags, the variation selectors, U+034F and U+1160, drawn as part of the character before them; the signs drawn over the digits after them, which take a cell; and every space and the blank Hangul fillers. The repository and the ref (SHOULD 2). #837's line named the `[![:print:]]` checks of both files: a UTF-8 locale's U+202E, U+200B, U+FEFF or U+00AD passes them, and the refusal after them echoed the value. Each now asks `misdrawn_characters` first, and refuses by the code point, saying nothing else of the value: `checked_repo`, `checked_version` (the version named and the newest release's tag), the option check and `latest_from_redirect`'s location in install.sh; `shape_checkout` and `ref_fault` (`$OPENREPOSHAPE_REF` and the record) in the command. First, because `[![:print:]]` is the locale's, so the words do not change with it. The words (NIT 3): "one that is invisible, is drawn as another character, ends a line or reorders the text around it", because U+00AD is drawn as a hyphen. The order (NIT 5): a tab before U+202E is refused as a control character, and a row holds it. Tests: `MISDRAWN` and the locale table take the twelve new code points and the tab row; the lifted sweep takes U+1D100 to U+1D1FF, U+E0000 to U+E007F and a sample of the planes past U+FFFF; `NOT_CONTROLS` takes a subdivision flag (tags U+E0020 to U+E007F); a test of each file refuses a value holding U+202E at every new site under `C` and `C.UTF-8`, through both entry points, with no request, nothing placed and neither stream holding it. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e stacked base of #858
…fuse is read again in the locale's own charset, so a GB18030, GBK or Shift_JIS name that holds no control character and nothing drawn as another is placed, and where nothing can read it the bytes decide as before (#858) #804's control check and #837's `misdrawn_characters` look for UTF-8's bytes under `LC_ALL=C`, and a multibyte charset that is not UTF-8 reads some of those bytes as text: GBK, GB18030 and CP936 read `0xC2 0x80` to `0xC2 0x9F` as 32 hanzi; GBK, Big5, EUC-JP and EUC-KR read U+00AD's two bytes as one character; and in Shift_JIS a kanji whose second byte is `0xC2` before a common kanji puts a C1 control's two bytes side by side (390 in 100,000 random pairs of common kanji, measured with Python's codecs). Such a directory, the default `$HOME/.local/bin` under a user name among them, was refused as holding a control character or a character drawn as another, in a locale where it holds neither. Both files now carry `locale_reading` (install.sh as a copy, held to one text by a test). Where the bytes refuse a directory, it asks `locale charmap` under the person's own locale, and where the answer is neither empty, UTF-8 nor ASCII, `iconv` reads the whole value from that charset into UTF-8; the reading is used only where it has fewer characters than the value has bytes, so a single-byte charset keeps the byte rule, as the issue's Direction says. The C1 bytes and `misdrawn_characters` are then asked of the reading, and `[[:cntrl:]]` of the bytes as typed. Only a value the bytes refuse is read again, so no name placed before is refused; with no `locale` or `iconv`, a charset either does not know, or a value the charset cannot read, the bytes decide, in #804's and #837's words. A lone `0x9B` stays placed, the limit #804 states: a terminal that reads UTF-8 draws it as a character it cannot read, only one set to an 8-bit charset acts on it, and a rule needs a UTF-8 validity step bash does not have. The launcher's comment says so. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…above) # Conflicts: # install.sh # openRepoShape # tests/test_install_sh.py # tests/test_openreposhape_command.py
Reviewer's GuideThe PR updates both the launcher and installer to reinterpret only previously rejected path bytes through the user’s supported multibyte locale, allowing names that locale reads as text while preserving byte-based safety checks and fallback behavior; extensive tests cover locale/iconv failure modes, charset-specific cases, and implementation parity. Sequence diagram for locale-aware directory installationsequenceDiagram
participant User
participant Launcher as openRepoShape
participant Locale
participant Iconv
participant Filesystem
User->>Launcher: --install with bin_dir
Launcher->>Launcher: misdrawn_characters(bin_dir)
alt bytes are refused
Launcher->>Locale: locale charmap
Locale-->>Launcher: charmap
Launcher->>Iconv: iconv -f charmap -t UTF-8
Iconv-->>Launcher: locale reading
Launcher->>Launcher: Check byte controls and misdrawn_characters(reading)
else no refused bytes
Launcher->>Launcher: Check bin_dir bytes
end
alt safety check refuses
Launcher-->>User: Existing refusal message
else safe directory
Launcher->>Filesystem: Place command in bin_dir
Filesystem-->>User: Installation result
end
Flow diagram for locale-aware bin directory validationflowchart TD
A[bin_dir as typed] --> B{C1 byte pair or misdrawn character?}
B -->|No| C[Use bytes as seen]
B -->|Yes| D[locale_reading]
D --> E{locale and iconv succeed?
Multibyte reading shortens value?}
E -->|No| C
E -->|Yes| F[Use locale reading as seen]
C --> G[Check control bytes and misdrawn characters]
F --> G
G --> H{Refused?}
H -->|Yes| I[Print existing refusal]
H -->|No| J[Install or place command]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-2, 2026-10-18T02:55:11Z) Independent review of #867 (#858) at The change is sound as written. Only a value the bytes already refuse is read again. Every fallback I probed refuses in #804's or #837's words. Where the locale's reading still holds a C1 control or a misdrawn character, it is refused and named, in install.sh before any request. The cost is a non-UTF-8 multibyte locale in a terminal that reads UTF-8. That is what the issue's Direction allows ("refuse nothing the locale reads as a letter"), and the PR body states it. The two SHOULDs below are test gaps. The code is right today, but two plausible regressions would ship green. SHOULD 1: no test pins that the locale's reading is itself checked.
SHOULD 2: no test pins that
NIT 3: no test pins "only a value the bytes refuse is read again".
NIT 4: a reading shorter than the value is taken as it is.
NIT 5: the code comments do not state the trade the PR body states.
NIT 6: the PR body cites a brief nobody can see.
NIT 7: line 2's reason is true but incomplete.
Checked and true
Could not verify
Lane: openreposhape-2 (openRepoShape-2) |
…_BIN_DIR` must write back into the value byte for byte, and tests hold that the reading is itself checked, that `locale charmap` is asked under the person's own locale and that a name the bytes pass is never read again (#858) Fix round 1 on rv858's review of #867 (READY, 2 SHOULDs, 5 NITs). SHOULD 1: no test had a multibyte charset read a name whose reading still holds a C1 control or a character drawn as another, so `locale_reading` printing anything at all (rv858's R1) shipped green. Two rows now do, through both files: a GB18030 hanzi of the C1 bytes beside U+009B in GB18030's own four-byte form (`0x81 0x30 0x83 0x37`), refused as a control character, and beside U+202E (`0x81 0x36 0xA7 0x31`), refused naming U+202E, install.sh before any request. SHOULD 2: the stub `locale` answered one charmap whatever it was asked under, so `LC_ALL=C locale charmap` (R2) shipped green. `put_a_locale`'s stub now answers as glibc's does, from the first of `$LC_ALL`, `$LC_CTYPE` and `$LANG` set: a marker `xx_XX.<charmap>` gives that charmap, `C.UTF-8` gives UTF-8, anything else `ANSI_X3.4-1968`; the cases run with `LANG` the marker and `LC_ALL` and `LC_CTYPE` empty, so bash runs in `C` (it says nothing of a `LANG` that is not installed). `C` and `C.UTF-8` stay where they mean something: as the person's own locale, in a test of three names refused there with `iconv` never asked; and as bash's own locale, in a test of `locale_reading` lifted out of each file with a `locale` function, `C.UTF-8` standing in for a multibyte locale of bash's own, where the readings are Python's codecs' and the count stays in bytes. NIT 3: a name the bytes pass (`café`) under a GB18030 locale, with an `iconv` that leaves a mark if it runs: placed, no mark (R3, the trigger replaced by `if :; then`). NIT 4: an `iconv` that exits 0 and prints nothing passed the count. The reading must now write back into the charset as the value, byte for byte (`iconv -f UTF-8 -t <charmap>`), in both copies; a broken `iconv` falls back to today's refusal, and a test holds it. The rows where `iconv` must not be asked use the marking `iconv` instead of one that reads anything, which the round trip would refuse anyway. NIT 5: `locale_reading`'s comment, both callers' and `export_path_line`'s say the trade: a multibyte locale that is not UTF-8, in a terminal that reads UTF-8, is handed C1 bytes it reads as letters. NIT 7: the launcher's comment names the ISO 8859 case (the charset is known and `a`, `0x9B`, `b` is placed) as a candidate, left. NIT 6 is the PR body's. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…BIN_DIR` and then fails gives no reading, so the first `iconv`'s exit is pinned beside the round trip (#858) Fix round 1 on rv858's review of #867, its last step. With the round trip in `locale_reading`, dropping the first `iconv`'s `|| return 1` (F07) survived the mutant table: a failing `iconv` that printed part of a reading, or nothing, is refused by the round trip anyway. One that prints the whole reading and then exits 1 is not: the round trip passes. A row of `REFUSED_AS_THE_BYTES_SAY` now has such an `iconv` (`READS_THEN_FAILS`, this machine's run in full, then `exit 1`) read a hanzi of the C1 bytes under GB18030, through both files: refused as a control character, as the bytes say, and install.sh before any request. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-2, 2026-10-18T04:51:55Z) Re-verification of #867 (#858) at The seven findings of the first review
NIT 8 (new): the round trip refuses names a correct
Checked and true
Could not verify
Lane: openreposhape-2 (openRepoShape-2) |
|
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 3 days and 13 hours by commenting |
…ter past ASCII is no control character under `C` either, as #760's byte rule decides in all seven checks that used `[![:print:]]`, and every control character is still refused (#940) #872 checked `$OPENREPOSHAPE_REPO` and `$OPENREPOSHAPE_REF` before every run but `--help`, with `[![:print:]]`, which under `C` and POSIX matches every byte past ASCII: a mirror at `/srv/café/openRepoShape.git`, or a ref `café`, was refused as "carrying a control character" by `--version`, a scaffold run, `--install` and `--preflight`, where `C.UTF-8` took it. `setup.sh`'s two guards and `install.sh`'s `checked_repo`, `checked_version` and option check held the same class. All seven now ask #760's rule, as `install_commands` holds the directory to it: under `LC_ALL=C`, in a subshell, `[[:cntrl:]]` or U+0080 to U+009F's two bytes, so one value gets one answer in every locale, and the launcher, `setup.sh`, `install.sh` and the package agree. The leading `-` arms stay, `misdrawn_characters` stays first, and no refusal's words change. A lone `0x9B` byte that is not UTF-8 now passes in both locales, the trade #867 took for the directory, which the comment at each place states. `setup.sh`, which has no copy of `misdrawn_characters`, also refuses U+2028 and U+2029 by their bytes, as it refused both in every locale before. The redirect's location in `install.sh` keeps `[![:print:]]`: it is the network's, and that class prints no lone C1 byte from it. One table, `CONTROL_RULE` in `tests/conftest.py`, holds the seven to one answer under `C` and `C.UTF-8`: `café`, a lone `0x9B` and a lone `0xE9` taken; an escape, U+009B, DEL, a tab, U+0085, U+0080, U+009F, a newline at the end and a lone `0xE9` then U+009B refused; U+202E, U+200B, U+2028 and U+2029 refused by code point. The three test files read it, and a repository or ref with `café` goes through every launcher run in both locales. Every arm of the rule dropped from any place fails a test, the seven the issue found surviving included. Closes #940 Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ter past ASCII is no control character under `C` either, as #760's byte rule decides in all seven checks that used `[![:print:]]`, and every control character is still refused (#940) (#986) #872 checked `$OPENREPOSHAPE_REPO` and `$OPENREPOSHAPE_REF` before every run but `--help`, with `[![:print:]]`, which under `C` and POSIX matches every byte past ASCII: a mirror at `/srv/café/openRepoShape.git`, or a ref `café`, was refused as "carrying a control character" by `--version`, a scaffold run, `--install` and `--preflight`, where `C.UTF-8` took it. `setup.sh`'s two guards and `install.sh`'s `checked_repo`, `checked_version` and option check held the same class. All seven now ask #760's rule, as `install_commands` holds the directory to it: under `LC_ALL=C`, in a subshell, `[[:cntrl:]]` or U+0080 to U+009F's two bytes, so one value gets one answer in every locale, and the launcher, `setup.sh`, `install.sh` and the package agree. The leading `-` arms stay, `misdrawn_characters` stays first, and no refusal's words change. A byte that is not UTF-8, as a lone `0x9B` or an overlong form of a control (`0xC0 0x9B`), now passes in both locales, the trade #867 took for the directory, which the comment at each place states. `setup.sh`, which has no copy of `misdrawn_characters`, also refuses U+2028 and U+2029 by their bytes, as it refused both in every locale before. The network's values get no such trade. `install.sh` still names the redirect's location only where the locale's `[![:print:]]` finds nothing in it, and it now takes the tag in a `…/releases/tag/<tag>` location only when that tag is printable ASCII under `LC_ALL=C`; any other tag names no release, in every locale, and `gh` is asked next. Without that, a tag holding a lone `0x9B` would pass `checked_version` by the trade, and its shape refusal would print the byte as it came from the network. A release tag is ASCII, so this refuses no real one. One table, `CONTROL_RULE` in `tests/conftest.py`, holds the seven to one answer under `C` and `C.UTF-8`: `café`, a lone `0x9B`, a lone `0xE9` and an overlong escape taken; an escape, U+009B, DEL, a tab, U+0085, U+0080, U+009F, a newline at the end and a lone `0xE9` then U+009B refused; U+202E, U+200B, U+2028 and U+2029 refused by code point. The three test files read it, and a repository or ref with `café` goes through every launcher run in both locales. A test holds the seven lines to one text, so a check without `LC_ALL=C`, which no locale installed here answers differently, fails it, and the redirect's test gives a tag with a lone `0x9B` or a letter past ASCII no answer but "names no release". Every arm of the rule dropped from any place fails a test, the seven the issue found surviving included. Independent review READY at H0 d4bba68 (#986 6277825631), with H0's tests and SonarCloud green. Refreshed by update-branch onto main af0e60f as 2a6554c, a merge whose tree 3e049cd lane 2's gate ran on (8 files: 165 passed; 961 passed; 612 passed), with SonarCloud green there. Landed on Brett Heap's word "merge v1.2.0 PRs once reviewed and green" (2026-10-18T14:45:28Z, #473 6251255596). Closes #940 Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-2 (openRepoShape-2)
Claim: #858 (comment)
Stacked on
$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841 (install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRholding a bidi override, U+200B, U+2028 or U+FEFF passes #804's control-character check and is echoed as typed, so the PATH note can show a directory as another #837),fix/837-bin-dir-format-chars, has landed asca47b71d. Its final heade7743c2d(the update-branch merge off2483cf1and main51414d2e) was squash-merged to main;git diff e7743c2d ca47b71dis empty, and both trees arec8f7d36b.$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841's headf2483cf1onto origin/mainb9b70692as1ba81b07, "Merge lane 2's install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841 head f2483cf (fix/837-bin-dir-format-chars) as the stacked base of install.sh and openRepoShape: the control-character check matches bytes, so a GBK or GB18030 name holding0xC2and a byte from0x80to0x9Fis refused as a control, and a lone0x9B, which an 8-bit terminal acts on, is placed #858". No conflict.e3baca96, on top of that merge.$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841 landed, lane 3's three steps:e9fe505b, "Merge lane 2's install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841 final head e7743c2 (fix/837-bin-dir-format-chars), landed as ca47b71". No conflict.2d1ff8a1, "Merge lane 2's install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841 squash ca47b71 (its final head e7743c2 is merged above)". It conflicted in install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841's four files and nowhere else;git diff e7743c2d ca47b71d -- <file>is empty for each, so each was taken as this branch's side.git merge origin/main(ca47b71d): already up to date, no commit.git diff --name-only ca47b71d 2d1ff8a1names onlyinstall.sh,openRepoShape,tests/test_install_sh.pyandtests/test_openreposhape_command.py, and its-U0changed lines are byte for byte those ofgit diff 1ba81b07 e3baca96. Main has since moved to5a7b6113(tests only, none of these four files);git merge-treeof it with2d1ff8a1is clean, and it is not merged here.e3baca96, the one commit after1ba81b07(the change), and18ae33b1and8bb85cbf, the commits after2d1ff8a1(fix round 1, on rv858's review). The two merges between them are the restack.Fix round 1, on rv858's review (READY, 2 SHOULDs, 5 NITs)
Two commits on top of
2d1ff8a1:18ae33b1(the fixes) and8bb85cbf(a test row the full mutant table asked for: F07, below). Each mutant named here is rv858's, or new with the fix, and is killed in the full table that follows.0x81 0x30 0x83 0x37), refused as a control character, and beside U+202E (0x81 0x36 0xA7 0x31), refused naming(U+202E); install.sh before any requestprintf '%s' "$reading"→printf '%s' x, both files):gb18030-reads-u+009b,gb18030-reads-u+202elocaleanswered one charmap whatever it was asked under$LC_ALL,$LC_CTYPEand$LANGset (a markerxx_XX.<charmap>,C.UTF-8, elseANSI_X3.4-1968); the marker cases run withLANG=xx_XX.<charmap>andLC_ALL,LC_CTYPEemptyLC_ALL=C locale charmap, both files): every placed casecaféunder GB18030, with aniconvthat leaves a mark if it runs: placed, no mark, both filesif :; then):no-byte-refused-never-read-again; and its install.sh twin, I12iconvthat exits 0 and prints nothing passed the count[ "$(printf '%s' "$reading" | iconv -f UTF-8 -t "$charmap" 2>/dev/null)" = "$1" ], in both copies (the identity test still passes). A test with such aniconv: today's refusal. The rows whereiconvmust not be asked now use the markingiconv, not one that reads anything, which the round trip would refuse anywayan-iconv-that-prints-nothing; G02 (back into UTF-8) and G03 (!=): every placed case. The round trip made F07 (the firsticonv's|| return 1dropped) survive at18ae33b1, since it also refuses what a failingiconvprinted in part;8bb85cbfaddsan-iconv-that-reads-then-fails(this machine'siconvrun in full, thenexit 1), which only that exit check refuseslocale_reading's comment, both callers' andexport_path_line's (which now says the C1 controls and the 41 are refused as the locale reads the name)a<0x9B>bis placedCandC.UTF-8, kept where they still mean something. bash cannot run in a locale that is not installed, so under the marker it runs inC, andC.UTF-8beside a GB18030LANGis a pair no person has. So:LC_ALL), they stay, in the new..._under_c_or_c_utf_8tests: three names, refused as the bytes say,iconvnever asked;test_locale_reading_counts_bytes_whatever_bash_itself_reads,locale_readinglifted out of each file with alocalefunction: thereC.UTF-8stands in for the multibyte locale bash runs in where GB18030 is installed, and it kills F10 (the count not underLC_ALL=C), which the marker cases, run inC, no longer can;Runs at
8bb85cbf(Linux, bash 5.2.21, Python 3.12.3, glibc iconv; as under "Tests"):CandC.UTF-8through both files; the lifted test, 2 files × 2 locales; the identity test). At18ae33b1it gave 798 passed (one row fewer, in two files);test_shape_doctor.py: 439 passed, 3 skipped, 753.91s;render-cli-reference.py --check: ok;git merge-treewith maind7befb91: clean, not merged.The full mutant table at
8bb85cbf. Each mutant is one edit in agit archivecopy of8bb85cbf, run against-k "multibyte_locale or reading_say or bytes_say or locale_reading or every_locale or unprinted_letter or link_in_it or symlink_check_names"(299 cases: the new ones and #804's and #837's directory cases) after an unmutated control (299 passed). A mutant oflocale_readingis made in both files alike, so the identity test is not what kills it; a caller's is made in one file (X the launcher, I install.sh). 46 mutants: 44 killed, 2 survived (X06, I06), named below. The runner, the mutants, the run log and the kill table are in this PR's attachments (mutants/run-fix1.log,mutants/kills-fix1.txt).2d1ff8a1's shell files, before fix round 1an-iconv-that-prints-nothing, both files (no round trip there)1ba81b07)café, the identity test, the lifted test, the U+202E reading rowlocalefailing ignoreda-locale-that-failscasestops at dropped (empty,UTF-8,utf8,ANSI_X3.4-1968,*ASCII*)iconv-unasked-under-<that answer>(the markingiconvruns); F03 and F05 also by..._under_c_or_c_utf_8underC.UTF-8andCiconv's|| return 1droppedan-iconv-that-reads-then-fails(8bb85cbf)caféLC_ALL=CC.UTF-8, both files-lefor-lt; no counta-single-byte-charset; the lifted test0x81, or to0xBEshift_jis-0xc2-0x81-0x58,shift_jis-0xc2-0x81-0x51; the lifted testprintf '%s' "$reading"→printf '%s' xgb18030-reads-u+009b,gb18030-reads-u+202e; the lifted testLC_ALL=C locale charmapcafé, andgb18030-reads-u+202e(refused, but as a control character)an-iconv-that-prints-nothing!=for=café,gb18030-reads-u+202eand the lifted test (G03 alsoan-iconv-that-prints-nothing)&&for||0x81, or to0x9Egb18030-0xc2-0x80,gb18030-0xc2-0x9f[[:cntrl:]]of the bytesa-tab-then-u+202e, #804's carriage-return link casemisdrawn_characters, asked of the bytes, not the readingseennever setNOT_CONTROLS(placed no more underset -u)no-byte-refused-never-read-again(the markingiconvruns)[[:cntrl:]]asked of the reading, not the bytes. Survived.2d1ff8a1, without the round trip,a-newline-at-the-endkilled them:$(…)dropped the newline from the reading. Now the round trip refuses that reading, and the bytes decide. The check stays on the bytes as typed, so it does not lean on the round tripDecisions
Line 1 (a GBK or GB18030 name refused as a control character): taken, as the Direction says, and for both arms alike. Where the bytes refuse a directory, the person's locale is asked how it reads them, and a multibyte charset's reading has the last word.
$OPENREPOSHAPE_BIN_DIRholding a control character before they name it, and install.sh refuses a:before its fetch, so a carriage return or an escape is no longer echoed as typed (#760) #804's C1 arm and install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841'smisdrawn_characterslook for UTF-8's bytes underLC_ALL=C. Measured with Python's codecs:0xC2 0x80to0xC2 0x9Fas 32 hanzi (U+8059 to U+807C in GB18030);0xC2 0xADare one character in GBK (U+9885), Big5 (U+7C2B), EUC-JP (U+8DB3, the "foot" of the surname Adachi) and EUC-KR;0xC2 0x9x(a kanji whose second byte is0xC2, then a JIS level-1 kanji), for example0x93 0xC2 0x90 0xED, a common two-kanji word. Big5 has 18 misdrawn straddles per 100,000, GBK and EUC-JP 2.cmd's output there, where the default$HOME/.local/binholds the Windows user name. A refusal with wider words ("or bytes that are one in UTF-8") would still refuse that default directory, so the words alone were not enough.locale_reading, in both files (install.sh's copy held to the launcher's text by a test):misdrawn_charactersnames something), so no name placed before is refused, and no other value runslocaleoriconv;locale charmapis asked under the person's ownLC_ALL,LC_CTYPEandLANG, outside theLC_ALL=Csubshell; an empty answer, UTF-8 (UTF-8,utf8) and ASCII (ANSI_X3.4-1968,US-ASCII) end it there;iconv -f <charmap> -t UTF-8reads the whole value, andiconv -f UTF-8 -t <charmap>writes the reading back into the value, byte for byte (fix round 1), so aniconvthat drops or changes a byte, or prints nothing, gives no reading;LANGleft in a terminal that reads UTF-8 (a common mismatch) its reading would let U+FEFF's three bytes through as three letters;misdrawn_charactersare asked of it, so a refusal names what the locale reads;[[:cntrl:]]is still asked of the bytes as typed (C0 and DEL are ASCII's in every charset a locale uses, and$(…)drops a newline at the end of the reading).locale(Alpine, the install.sh tests' own$PATH), alocalethat fails or answers nothing, a locale that is not installed (glibc answersANSI_X3.4-1968), noiconv, a charset iconv does not know, a value the charset cannot read, a single-byte charset. The only names it places that were refused before are names a multibyte charset reads as text, as the issue's Direction says: "for another multibyte charset, either refuse nothing the locale reads as a letter".a<0xC2 0x9B>2Jbanda<0xC2 0x9D>0;TITLE<0xC2 0x9C>bare placed, and four lines on stdout then carry CSI2J(erase the screen) or OSC 0 (set the title) in UTF-8, which such a terminal acts on (rv858's measurement). Nobody keeps that mismatch long (every non-ASCII name they have is drawn wrong), and it is what the Direction asks for: "refuse nothing the locale reads as a letter". The comments in both files now say so (fix round 1). The words of both refusals stay as install.sh and openRepoShape: both refuse a$OPENREPOSHAPE_BIN_DIRholding a control character before they name it, and install.sh refuses a:before its fetch, so a carriage return or an escape is no longer echoed as typed (#760) #804 and install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841 left them.Line 2 (a lone
0x9Bplaced): left, as the documented limit.0x9B: it is not UTF-8, and a decoder draws it as a character it cannot read. The C1 controls such a terminal acts on are U+0080 to U+009F, two bytes each, which the C1 arm refuses. Only a terminal set to an 8-bit charset with C1 controls (ISO 8859) acts on the one byte. (Not measured on a terminal: none here.)0x9Bbyte, U+011B among them), and bash has no validity builtin;iconv -f UTF-8 -t UTF-8may be missing. UnderC, where the charset is unknown, refusing a lone byte would refuse GBK and Shift_JIS names (every JIS level-1 kanji starts with a byte from0x88to0x98). And0x9Bis not alone:0x85(NEL) ends a line and0x9D(OSC) starts a terminal command, so the rule would be the whole lone C1 range.printf 'a\x9bb' | iconv -f ISO-8859-1 -t UTF-8gives61 c2 9b 62. But the name holds no byte the check refuses, so nothing reads it again, and under anISO-8859-1localea<0x9B>bis placed (rv858's measurement). Reading every non-ASCII name again under a known 8-bit charset would close it, at the cost of alocalecall for every such name.What changed
openRepoShape:locale_reading, aftermisdrawn_characters, with a comment that says what it reads, when, and when the bytes decide. Ininstall_commands:seenis the directory as typed, or, where its bytes hold a C1 control's two bytes ormisdrawn_charactersnames something in them andlocale_readingsucceeds, the locale's reading;[[:cntrl:]]of the bytes as typed and the C1 bytes ofseen;misdrawn_charactersis asked ofseen.$OPENREPOSHAPE_BIN_DIRholding a control character before they name it, and install.sh refuses a:before its fetch, so a carriage return or an escape is no longer echoed as typed (#760) #804's comment now says a C1 control is refused in every locale "unless the locale reads those bytes as text", and the new paragraph says a lone C1 byte is placed, as before, and why.install.sh: the same function, copied as it is, and the same three changes inmainon$bin_dir, before any request.$OPENREPOSHAPE_BIN_DIRholding a control character before they name it, and install.sh refuses a:before its fetch, so a carriage return or an escape is no longer echoed as typed (#760) #804's or install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRholding a bidi override, U+200B, U+2028 or U+FEFF passes #804's control-character check and is echoed as typed, so the PATH note can show a directory as another #837's own words.Measured by a probe (
probes/probe.shin this PR's attachments): alocalefirst on$PATHthat answerscharmapas shown, this machine'siconv, aghstub that exits 97,GH_TOKEN=bogus, an emptyGH_CONFIG_DIR, acurlstub that logs and exits 22, bash's ownLC_ALL=C.UTF-8,<H>for the probe's directory, andOPENREPOSHAPE_BIN_DIR=<H>/a+ the bytes +b:Where it is placed, the command's lines name the directory as typed, as for any other name:
and the refusals, before and after, are the words #804 and #837 gave them, for example:
Tests
No GB18030, GBK or Shift_JIS locale is installed here or on CI, and none can be built. So
put_a_localeputs alocalefirst on$PATHthat answerslocale charmapas glibc's does, from the first of$LC_ALL,$LC_CTYPEand$LANGthat is set (fix round 1): a marker localexx_XX.<charmap>gives that charmap,C.UTF-8givesUTF-8, anything elseANSI_X3.4-1968. It can also fail after answering, or exit 127 as a missing program does. Beside it is this machine'siconv, or one that exits 127, one that prints nothing and exits 0, one that runs this machine's and then exits 1, or one that leaves a mark if it runs. The person's locale isLANG=xx_XX.<charmap>withLC_ALLandLC_CTYPEempty; bash says nothing of aLANGthat is not installed, and runs inC.tests/test_openreposhape_command.pytest_a_bin_dir_a_multibyte_locale_reads_as_text_is_placed(overPLACED_AS_THE_LOCALE_READS)0xC2 0x80,0xC2 0x9Band0xC2 0x9Funder GB18030 (the first, the issue's and the last of the 32 hanzi);0xC2 0xADunder GBK; U+2028's bytes under Shift_JIS; two Shift_JIS straddles (0xC2 0x81 0x58,0xC2 0x81 0x51) whose reading holds a0x80and a0xBF, the edges of the bytes that continue a character; andcafé, which the bytes pass, under GB18030 with aniconvthat leaves a mark if it runs: exit 0, the command placed, no marktests/test_install_sh.pytest_the_installer_places_a_directory_a_multibyte_locale_reads_as_texttests/test_openreposhape_command.pytest_a_bin_dir_is_refused_as_its_bytes_or_its_reading_say(overREFUSED_AS_THE_BYTES_SAY)0x81 0x30 0x83 0x37) or U+202E (0x81 0x36 0xA7 0x31) in GB18030's own form, refused as a control character or naming U+202E; and the bytes deciding: alocalethat fails, or is not there; noiconv; aniconvthat prints nothing, or prints the whole reading and then exits 1; a byte GB18030 cannot read; Latin-1 and U+FEFF's bytes (single-byte); a newline at the end; and a markingiconvnever asked wherelocaleanswers nothing,UTF-8,utf8,ANSI_X3.4-1968orUS-ASCII: exit 2, the words, nothing placed, never echoed, no marktests/test_install_sh.pytest_the_installer_refuses_as_the_bytes_or_the_reading_saytests/test_openreposhape_command.pytest_a_bin_dir_is_refused_as_its_bytes_say_under_c_or_c_utf_8(overREFUSED_UNDER_C_OR_UTF_8)LC_ALL=CorC.UTF-8(ANSI_X3.4-1968orUTF-8from the stub):0xC2 0x9B,0xC2 0xADand U+2028's bytes refused in #804's and #837's words,iconvnever askedtests/test_install_sh.pytest_the_installer_refuses_as_the_bytes_say_under_c_or_c_utf_8tests/test_openreposhape_command.pytest_locale_reading_counts_bytes_whatever_bash_itself_reads[openRepoShape, install.sh]locale_readinglifted out of each file, with alocalefunction, under bash's ownCandC.UTF-8(standing in for the multibyte locale bash itself runs in where GB18030 is installed): each placed name's reading is Python's codec's, and Latin-1's U+FEFF bytes and a byte GB18030 cannot read give nonetests/test_openreposhape_command.pytest_install_sh_carries_the_launchers_locale_reading_as_it_islocale_readingis the launcher's, byte for byte (throughshell_function, whichmisdrawn_checknow uses too)At
2d1ff8a1, before fix round 1: the new cases over the stacked base's two shell files (1ba81b07'sinstall.shandopenRepoShape, these tests): 29 failed (the 28 placed cases and the identity test), and the 56 refused cases pass, as they should: they hold that nothing else changed.Mutants at
2d1ff8a1, before fix round 1. Each one is a single edit in agit archivecopy of2d1ff8a1, run against the new cases with #804's and #837's directory cases (-k "multibyte_locale or nothing_reads or locale_reading or every_locale or unprinted_letter or link_in_it or symlink_check_names": 321 cases) after an unmutated control (321 passed). A mutant oflocale_readingis made in both files alike, so the identity test is not what kills it; a mutant of a caller is made in one file (X the launcher, I install.sh). 37 mutants, 37 killed, no survivor. The runner, the mutants, the run log and the kill table are in this PR's attachments, undermutants/.localefailing ignored (F01)a-locale-that-fails, both filescasestops at dropped: empty,UTF-8,utf8,ANSI_X3.4-1968,*ASCII*(F02 to F06)iconv-unasked-under-<that answer>: theiconvthat would read anything is asked, and the name is placediconvfailing ignored (F07)no-iconv,a-byte-gb18030-cannot-readLC_ALL=C(F10)C.UTF-8-lefor-lt(F11); no count (F12)a-single-byte-charset(Latin-1 would let U+FEFF's bytes through)0x81(F13), or to0xBE(F14)shift_jis-0xc2-0x81-0x58(a0x80in the reading),shift_jis-0xc2-0x81-0x51(a0xBF)&&for `0x81, or to0x9E(X03, X04, I03, I04)gb18030-0xc2-0x80,gb18030-0xc2-0x9flocaleanswersUTF-8underC.UTF-8)[[:cntrl:]]asked of the reading (X06, I06)a-newline-at-the-end[[:cntrl:]]of the bytes (X07, I07)a-newline-at-the-end, #837'sa-tab-then-u+202e, #804's carriage-return link casemisdrawn_characters, asked of the bytes and not the reading (X08, X09, I08, I09)seennever set when nothing is read (X11, I11)NOT_CONTROLS, placed underset -uno moreRuns (Linux, bash 5.2.21, Python 3.12.3, glibc iconv;
env -u GH_TOKEN -u GITHUB_TOKEN GH_CONFIG_DIR=<empty> /usr/bin/python3 -m pytest -q -p no:cacheprovider, one basetemp,df -ichecked first):1ba81b072d1ff8a1(restacked on mainca47b71d)test_openreposhape_command.py,test_install_sh.py,test_install_sh_filter_repo.py,test_openreposhape_package.py,test_repo_hygiene.py,test_cli_reference.py,test_agents_md_codes.py,test_readme_windows_quoting_sentence.pytest_shape_doctor.py(it runs the launcher's--doctor)render-cli-reference.py --check: ok. shellcheck 0.9.0: the same 8 findings before and after, none in the new lines.Not changed
$OPENREPOSHAPE_BIN_DIRholding a control character before they name it, and install.sh refuses a:before its fetch, so a carriage return or an escape is no longer echoed as typed (#760) #804's and install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRholding a bidi override, U+200B, U+2028 or U+FEFF passes #804's control-character check and is echoed as typed, so the PATH note can show a directory as another #837's refusals, and their order: a control character is still refused first.0x9B, and0x80to0x9Falone): placed, as before (line 2's decision, above).0xC2 0xAD): refused as before, because its reading has as many characters as it has bytes.iconvreads but writes back as other bytes): refused as the bytes say, as before, because the round trip does not give the name back (rv858's NIT 8, install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIRthe bytes refuse is read again in the locale's own charset, so a GB18030, GBK or Shift_JIS name that holds no control character and nothing drawn as another is placed, and where nothing can read it the bytes decide as before #867 issuecomment-6245826032). GB18030, GBK, Shift_JIS, EUC-JP and EUC-KR have none.0x81 0x36 0xA7 0x31there, and U+009B0x81 0x30 0x83 0x37, by Python's codec): placed, as before. The locale is asked only where the UTF-8 bytes matched, so that no name placed before is refused. A candidate, not install.sh and openRepoShape: the control-character check matches bytes, so a GBK or GB18030 name holding0xC2and a byte from0x80to0x9Fis refused as a control, and a lone0x9B, which an 8-bit terminal acts on, is placed #858's record.$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841'smisdrawn_characterscalls for them are unchanged, and in a multibyte locale they refuse a local path or a ref holding those bytes as before. None of them is$OPENREPOSHAPE_BIN_DIR, and a github.com owner/name holds no such byte. A candidate.--help, README, the handbook,AGENTS.md,docs/cli.md: no sentence states these bytes (git grep: onlydocs/cli.md's one-line description of the variable, and release notes).localesays the charset), bash 3.2 (macOS), Git Bash on Windows (these cases skip there, as install.sh and openRepoShape: both refuse a$OPENREPOSHAPE_BIN_DIRholding a control character before they name it, and install.sh refuses a:before its fetch, so a carriage return or an escape is no longer echoed as typed (#760) #804's and install.sh and openRepoShape: a$OPENREPOSHAPE_BIN_DIR, the repository or a ref holding a bidi control, a character of no width, a soft hyphen or U+2028 is refused by its code point before any request and never echoed, so no line shows the value as another #841's do; in Git Bash's default UTF-8 charmaplocale_readingstops at its firstcase), any real terminal.Closes #858
🤖 Generated with Claude Code
Summary by Sourcery
Make installation directory validation aware of how the user's multibyte locale interprets refused byte sequences while preserving safe fallback checks.
Bug Fixes:
Enhancements:
Tests: