Skip to content

adopt-project.py: under LC_ALL=C PYTHONUTF8=0, plan, check and execute hand git and gh each name, url and branch and the leg description as the UTF-8 bytes it spells, so none of them ends in a UnicodeEncodeError before any leg exists (#653) - #727

Merged
brettheap merged 6 commits into
mainfrom
fix/653-adopt-c-locale-encoding
Oct 16, 2026

Conversation

@brettheap

@brettheap brettheap commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Lane: openreposhape-1 (openRepoShape-1)
Claim: #653 (comment)

What

Under LC_ALL=C PYTHONUTF8=0, adopt-project.py now hands git and gh every name, url and branch it read from git, and the leg description it writes, as the UTF-8 bytes each spells, as #614 did for git rm (#527). Each place below used to end in a UnicodeEncodeError traceback, exit 1, before any leg existed.

  • L1-33, GitHub mode. gh repo create --description carries the tool's own em dash (Northwind — spec leg, extracted from testorg/Northwind with history), so every execute against GitHub stopped at the first leg, an all-ASCII source included. Both legs are now created. The description and the gh pr create --body after it (the split's message, which names every moved path) reach gh as written, byte for byte.
  • L1-34, plan. The git grep that finds code naming a spec directory (cross_leg_references) was handed Ärger/, or a code path such as Werkzeuge-Ü/, as text, and plan wrote no plan. It now writes the plan UTF-8 mode writes, byte for byte.
  • L1-35, check and execute. _resolved handed git config a relative submodule url (../Ünterlagen), or a source remote under a directory outside ASCII, as text. So did _default_remote for a remote whose name is Ürsprung. Past those, same_as asked realpath of such a url, and adopt-project.py says adoption verified when a submodule the plan keeps in a leg has a url that names no repository (a relative url that resolves alike from the source and the leg, or an absolute one), so the recursive clone its NEXT: line prints fails; nothing asks whether the url answers #418's git ls-remote was handed it. Each now goes as bytes, and check and execute say what UTF-8 mode says.
  • Source.open, forwarded from w425's probe. It handed git rev-parse a UTF-8 default branch such as café as text, so plan, check and execute died before anything was said of the branch. Now a source on café is refused unsafe-value for the tracking branch, as in UTF-8 mode, or adopted past it with --tracking-branch main.

The ruling is Brett Heap's of 2026-10-12, for #527: "pass git the exact bytes the tree holds, under any locale". His 2026-10-16 answer to #457's triage, "In scope, file D4 (Recommended)", puts this locale in scope here.

Why (repro on main)

Probed on main at a524542 (the review found the new tests failing the same way, at the same functions, on 30676c9), with a stub gh first on PATH and a throwaway git config that sends https://github.com/ to a local directory (no network):

run, under LC_ALL=C PYTHONUTF8=0 main this PR
GitHub-mode execute of an all-ASCII source UnicodeEncodeError ('—') in _create_leg_remote, exit 1, no repository made both legs made, adoption verified, exit 0
the same, with Überblick.md moving to the spec leg the same the pull request opened, its body the split commit's message byte for byte
plan of a source whose Ärger/ (spec) is named by Werkzeuge-Ü/run.py and Übung.py (code) UnicodeEncodeError in cross_leg_references, exit 1, no plan the plan UTF-8 mode writes, byte for byte; its follow-ups name both files
check, ../Ünterlagen kept in the code leg, source on GitHub UnicodeEncodeError in _resolved, exit 1 plan ok and UTF-8 mode's NOTEs, exit 0
check, a source whose origin is <dir>/Ünter/testorg/Thing.git UnicodeEncodeError in _resolved, exit 1 UTF-8 mode's plan-submodule-relative-url finding, exit 1
check, a source whose branch tracks a remote named Ürsprung UnicodeEncodeError in _config_value, exit 1 UTF-8 mode's finding, naming Ürsprung, exit 1
check, a source with no remote in <dir>/Ünter/ the finding names the directory \udcc3\udc9cnter the finding names Ünter, as UTF-8 mode does
execute --local-remote-dir, ../Ünterlagen naming one repository from the source and from the leg UnicodeEncodeError in _resolved, exit 1 adoption verified, exit 0
plan of a source on café UnicodeEncodeError in Source.open, exit 1 REFUSED unsafe-value, exit 2, as in UTF-8 mode; with --tracking-branch main, the plan UTF-8 mode writes

How

Tests

tests/test_adopt_c_locale_bytes.py: 8 tests, 17 cases. Every case fails on main at a524542, and again at b618ec1, and passes here, on this head, which holds main b618ec1. All but one run adopt-project.py, or the helper itself, in a child under LC_ALL=C PYTHONUTF8=0. Each of those skips on Windows, and where that locale still encodes file names as UTF-8 (#614's c_locale_or_skip); the one that runs in this process runs everywhere. What a run says is compared with what the same run says in UTF-8 mode (LC_ALL=C PYTHONUTF8=1), once the C locale's \xdc-style escapes are undone, and the comparison requires at least one REFUSED, FINDING or NOTE line.

Revert table. Each change reverted alone in the final file, run over the new file (17 cases; 17 pass with the change). The first 13 rows were measured before the two helper tests were added; those call only _git_out_exact, which none of the 13 touches. The last two rows were measured on this head:

change reverted fails
gh repo create through run 2: both GitHub cases
gh pr create through run 1: the pull request's body
git grep handed text 1: the plan case
_resolved's url through run 2: check url, and the execute case
_resolved's base through run 3: check origin and own-directory, and the execute case
the source's own directory not _git_spellinged 1: check own-directory (the finding said \udcc3\udc9c)
the working tree not _git_spellinged 1: check working-tree
same_as's realpath of text 1: the execute case
_answers' git ls-remote --get-url of text 1: the execute case
_answers' git ls-remote of text 1: the execute case
the leg's remote not _git_spellinged 1: the execute case (the refusal named <dir>/\udcc3\udc9cnter)
_config_value's key handed text 1: check remote-name
Source.open's git rev-parse through git_out 3: every café case
_run_exact never calling run none of the new cases. 8 existing tests that stand in for run to catch a gh call fail, and 10 error: test_adopt_filter_repo_failure.py, test_adopt_late_failure.py and test_adopt_split_branch.py
_git_out_exact never calling git_out none of the 16 then, nor test_adopt_manifest_values.py: no test stands in for git_out on Source.open's path. The hand-off keeps every UTF-8 run on git_out itself
_git_out_exact encoding strictly, never through git_out (#425's first risk) 2: both caf\udce9 cases, with UnicodeEncodeError
_git_out_exact handing git_out bytes (#425's second risk) 2: both missing cases, with TypeError: sequence item 0: expected str instance, bytes found from git_out's refusal
_git_out_exact's refusal without spelled (git's lines raw, the review's F1) 2: the gone case and the refusal comparison
_git_out_exact's refusal without shown_path (the folder raw) 1: the refusal comparison

Focused runs. Run as the coordinator asked after a session exit voided the full runs, and again after the review, with /usr/bin/python3 -m pytest, a stub gh first on PATH, GH_TOKEN a dummy, an empty GH_CONFIG_DIR and no GIT_ALLOW_PROTOCOL. Each log names its head and dirty 0, at its start and its end.

  • At this head, 0274b68, which is main b618ec1 merged in (217a8f7) and the refusal fix: the new file, tests/test_adopt_git_reads.py, tests/test_scaffold_leg_path_collisions.py, tests/test_git_output_decoding.py and tests/test_stdout_encoding.py. With git filter-repo on PATH: 258 passed, 1 skipped, 0 failed. With it hidden: 252 passed, 7 skipped, 0 failed.
  • At 04c6843, before main was merged in, with this PR's own changes to adopt-project.py except the refusal fix, 13 files were run:
    • the new file;
    • the C-locale tests that run adopt-project.py: test_adopt_git_rm_bytes.py, test_adopt_entry_paths.py, test_git_output_decoding.py and test_stdout_encoding.py;
    • tests/test_shape_doctor.py and tests/test_scaffold_leg_path_collisions.py;
    • the files that drive the changed call sites under UTF-8: test_adopt_github_exits.py, test_adopt_relative_submodule_url.py, test_adopt_literal_names.py, and test_adopt_late_failure.py, test_adopt_filter_repo_failure.py and test_adopt_split_branch.py, which stand in for run.
    • With git filter-repo on PATH: 931 passed, 4 skipped, 0 failed. With it hidden (PATH=/usr/local/bin:/usr/bin:/bin behind the stub gh): 877 passed, 58 skipped, 0 failed.
  • At cc652b1, which added only the first helper test, the new file and adopt-project.py _default_branch reads the source's branch name through git_out, which decodes strictly, so a source whose HEAD (or origin/HEAD) names a branch that is not UTF-8 ends plan, check and execute in a UnicodeDecodeError traceback, exit 1, from Source.open, with no plan written and no refusal naming the branch #425's tests/test_adopt_git_reads.py were run: 37 passed with git filter-repo, and 31 passed, 6 skipped hidden.
  • Earlier, at 9b8e176 (the first commit alone), all 88 files that run or import adopt-project.py passed with git filter-repo hidden: 6,059 passed, 360 skipped.
  • The full targeted set was not re-run at this head. CI's full Linux suite runs on this PR.

Main, and #425. Main at b618ec1 is merged into this branch (217a8f7, clean, no conflict). The trial merges below were made with git merge-tree and commit-tree in a scratch clone, and nothing of them was pushed:

The fix holds whichever of #425 and #653 lands first. #425 keeps a branch byte that is not UTF-8 as a lone surrogate, which _path_argument and os.fsencode both write back as that byte, through surrogateescape. A UTF-8 name such as café is still text after #425, and only this change hands it to git as its bytes. On the merge, plan --tracking-branch main under the C locale works for both a caf\xe9 and a café default branch.

Windows

CI is Linux only since #597. On Windows, _path_argument, _git_spelling, _run_exact and _git_out_exact each leave the text as it is and call what was called before, so nothing changes there. The C-locale tests skip on Windows: a C locale is a POSIX idea, and Windows hands a command line to CreateProcessW as UTF-16. The comparison of the two refusals runs everywhere; on Windows _git_out_exact always calls git_out, so the two are one. Every name the tests make (Ünter, Ünterlagen, Ürsprung, Ärger, Werkzeuge-Ü, Übung.py, Überblick.md, café, line\u2028separator) is legal on NTFS. They compare names as the bytes git records, and never count a \n in a file a helper wrote.

Not in this PR

  • L1-36, the plan table's padding under the C locale. _print_entries (adopt-project.py:4212 at this head) pads _shown(entry.path) to 34 characters before stdout escapes it, so café.md (caf\xe9.md, 4 characters wider) shifts the leg column. It is a display fix, not this one, and stays on Tracking: adopt-project.py, converting an existing repository in place #457.
  • L1-38, macOS NFD names and a legacy 8-bit locale, are not measured. The review container has only the C, C.utf8 and POSIX locales, and no macOS.
    • By the code: every argument this PR hands as bytes is UTF-8 text git printed or this tool wrote, or a disk path _git_spelling turned into such text first, so a Latin-1 locale gets the bytes the disk or the tree holds.
    • macOS always encodes a file name as UTF-8, so the C locale's failure does not arise there, and the tests skip there.
  • A plan written in UTF-8 mode, whose source directory is outside ASCII, cannot be checked under the C locale. The plan records …/Ünter/Thing. Under the C locale Path.exists() cannot hold that name and says False, so Source.open takes it for org/repo and dies in Source.open's gh repo clone (adopt-project.py:1561 at this head) with UnicodeEncodeError. A plan written under the C locale records the bytes as surrogates, and reads back fine in either locale. This is a new line for Tracking: adopt-project.py, converting an existing repository in place #457's checklist.

Known limits

  • _run_exact and _git_out_exact call run and git_out only where the locale hands every argument alike. A test that stands in for either under the C locale therefore does not see these calls. No test does so today.
  • Every other git and gh call that plan, check and execute make still hands text, as read on this head, because nothing outside ASCII reaches it, or only a path the disk gave does. Such text is: names checked_value admits (organisation, repository, tracking and split branches, leg mounts), the shape's own file names, object ids, and paths os.fsdecode made, which subprocess hands back as the same bytes in any locale.

Closes #653

🤖 Generated with Claude Code

Summary by Sourcery

Preserve UTF-8 byte spelling across Git and GitHub operations so adoption workflows behave consistently under non-UTF-8 locales.

Bug Fixes:

  • Ensure plan, check, and execute pass non-ASCII Git and GitHub arguments using the exact UTF-8 bytes they represent under non-UTF-8 locales.
  • Prevent UnicodeEncodeError failures when handling non-ASCII branch names, paths, URLs, remote names, GitHub descriptions, and pull request bodies.

Enhancements:

  • Add locale-sensitive command helpers that preserve existing command behavior while providing consistent Git failure reporting.
  • Add comprehensive C-locale coverage for GitHub execution, planning, checking, execution, branch handling, and command error formatting.

Tests:

  • Add regression tests covering 17 non-ASCII byte-handling cases across plan, check, and execute workflows.

Brett Heap and others added 2 commits October 16, 2026 12:47
…e, a url and the leg description as the UTF-8 bytes each spells, so under `LC_ALL=C PYTHONUTF8=0` `gh repo create`'s em dash, `plan`'s `git grep` and `check`'s submodule url no longer end in a `UnicodeEncodeError` traceback before any leg exists (#653)

On POSIX `subprocess` encodes a `str` argument with the locale's codec when Python is not in UTF-8 mode, and under `LC_ALL=C PYTHONUTF8=0` that codec is ASCII. #614 (#527) made `execute` hand `git rm` the bytes the tree holds; three places still handed git or gh text, and each ended in a traceback, exit 1, before any leg existed. This carries out the 2026-10-12 ruling for #527, "pass git the exact bytes the tree holds, under any locale", and Brett Heap's 2026-10-16 answer to #457's triage, "In scope, file D4".

- L1-33: `gh repo create --description` carries the tool's own em dash, so every GitHub-mode `execute` stopped at the first leg, an all-ASCII source included. The `gh pr create --body` after it is the split's message, which names the moved paths.
- L1-34: `plan`'s `git grep` (`cross_leg_references`) was handed a spec directory or a code entry outside ASCII as text, and `plan` wrote no plan.
- L1-35: `_resolved` handed `git config` a relative submodule url outside ASCII, or a source remote outside ASCII, as text, and `check` and `execute` died. So did `_default_remote`, asking `git config` for the url of a remote named outside ASCII. Past them, `same_as` asked `realpath` of such a url, and #418's `git ls-remote` was handed it, and both died the same way.

The change:
- `_run_exact(command, cwd)` is `run`, with each argument handed as `_path_argument` (#614's helper) makes it: UTF-8 bytes on POSIX, text on Windows. Wherever the locale's codec already gives those bytes, which is every UTF-8 locale, it calls `run` itself as the call site did, so nothing changes there, the tests' stand-ins for `run` included. `gh repo create`, `gh pr create` and `_resolved`'s two `git config` calls use it.
- `cross_leg_references` hands `git grep` the spec directory and the code paths through `_path_argument`, and so do `_config_value` for its key, `_answers` for its two `git ls-remote` calls, and `same_as` for `realpath`.
- `_git_spelling(path)` spells a path read from the disk as `_git_path` spells one git printed, the same bytes. `_default_remote` spells the source's directory that way (the base, or the working tree a relative answer is joined to), and `_relative_url_findings` the leg's remote, so that each goes to git as the bytes the disk holds under any locale, a Latin-1 one included, and is named as UTF-8 mode names it (`Ü`, not `\udcc3\udc9c`).
- The description and the body are handed to gh as they were written.

Tests: tests/test_adopt_c_locale_bytes.py adds 5 tests (9 cases), all failing on main at a524542, each in a child under the C locale and skipped on Windows and where that locale still encodes file names as UTF-8. A GitHub-mode `execute` (tests/test_adopt_github_exits.py's harness, with a `gh` stand-in that keeps each call's arguments as bytes) hands `gh repo create` the description and `gh pr create` the split's message as UTF-8, byte for byte the commit's; `plan` writes the plan UTF-8 mode writes and finds both code paths that name `Ärger/`; `check` says what UTF-8 mode says for a url, a remote, a remote's name, a source directory and a working tree outside ASCII; and `execute --local-remote-dir` refuses as UTF-8 mode does and adopts `../Ünterlagen`. Each change reverted alone fails at least one of them. A `_run_exact` that never calls `run` passes them, and fails 8 existing tests (and errors 10) that stand in for `run` to catch a `gh` call.

Lane: openreposhape-1 (openRepoShape-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nch as the bytes git keeps it, so under `LC_ALL=C PYTHONUTF8=0` a source on `café` is refused `unsafe-value`, or adopted past it with `--tracking-branch`, and `plan`, `check` and `execute` no longer end in a `UnicodeEncodeError` before anything is said of the branch (#653)

w425's probe, forwarded by the coordinator: `Source.open` read the source's default branch with `_default_branch` and handed it to `git rev-parse` through `git_out` as text. Under the C locale a UTF-8 name such as `café` raised `UnicodeEncodeError` there, in `plan`, `check` and `execute` alike, whatever `--tracking-branch` said.

- `_git_out_exact(args, cwd)` is to `git_out` what `_run_exact` is to `run`: `git_out` itself wherever the locale hands every argument alike, which is every UTF-8 locale; elsewhere git is run with `_path_argument` of each argument, and a git that refuses is refused as `git_out` refuses it, `git-failed`, naming the command as the person reads it. `git_out` is `scripts/repo_shape.py`'s, a pinned shape copy, and it cannot take a bytes argument: its refusal joins the arguments as text.
- `Source.open` asks `git rev-parse` through it.

It holds whichever of #425 and #653 lands first. #425 reads the name as bytes and keeps a byte that is not UTF-8 as a lone surrogate, which every locale's codec writes back as that byte; a UTF-8 name is still text, which only this change hands git as its bytes.

Tests: tests/test_adopt_c_locale_bytes.py adds one test, 3 cases, each failing on 9b8e176: under the C locale `plan` of a source on `café` is refused `unsafe-value` as in UTF-8 mode; with `--tracking-branch main` it writes the plan UTF-8 mode writes, byte for byte, and `check` names `café` as UTF-8 mode does; and where git refuses `git rev-parse café`, the refusal is UTF-8 mode's `git-failed`.

Lane: openreposhape-1 (openRepoShape-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 16, 2026

Copy link
Copy Markdown

Reviewer's Guide

Makes adopt-project.py robust under non-UTF-8 POSIX locales by preserving the exact UTF-8 bytes for Git/GitHub arguments and Git-derived paths, while retaining existing command/error behavior; adds regression tests covering plan, check, and execute workflows.

Sequence diagram for locale-safe Git and GitHub execution

sequenceDiagram
    participant AdoptProject
    participant ExactRunner
    participant GitOrGh

    AdoptProject->>ExactRunner: _run_exact(command, cwd)
    ExactRunner->>ExactRunner: _the_locale_hands_it_alike(argument)
    alt Locale already preserves arguments
        ExactRunner->>GitOrGh: run(command)
    else Non-UTF-8 locale
        ExactRunner->>GitOrGh: subprocess.run(_path_argument(argument))
    end
    GitOrGh-->>ExactRunner: UTF-8 stdout or failure
    ExactRunner-->>AdoptProject: Result or CommandFailed
Loading

Sequence diagram for locale-safe source branch resolution

sequenceDiagram
    participant SourceOpen
    participant ExactGitRunner
    participant Git

    SourceOpen->>SourceOpen: _default_branch(path)
    SourceOpen->>ExactGitRunner: _git_out_exact([rev-parse, branch], path)
    ExactGitRunner->>Git: git rev-parse _path_argument(branch)
    Git-->>ExactGitRunner: Commit or git failure
    ExactGitRunner-->>SourceOpen: Commit or git-failed refusal
Loading

File-Level Changes

Change Details Files
Add locale-safe subprocess argument handling for Git and GitHub commands.
  • Introduce wrappers that pass UTF-8 byte spellings on POSIX while preserving existing run/git_out behavior when locale encoding is equivalent.
  • Use exact-byte execution for GitHub repository creation, pull-request creation, URL resolution, and default-branch lookup.
  • Pass Git-derived paths, URLs, remote names, grep arguments, realpath inputs, and ls-remote URLs through byte-preserving conversion.
  • Normalize disk paths to Git spelling when combining them with Git-reported relative URLs, preserving both command bytes and diagnostic text.
adopt-project.py
Add comprehensive C-locale regression coverage for plan, check, and execute.
  • Compare LC_ALL=C PYTHONUTF8=0 behavior with UTF-8 mode across non-ASCII names, URLs, remotes, branches, paths, descriptions, and pull-request bodies.
  • Record fake gh arguments as raw bytes and verify descriptions and commit messages are passed byte-for-byte.
  • Cover GitHub execution, cross-leg reference discovery, relative URL resolution, local remote execution, and non-ASCII default-branch handling.
  • Verify equivalent findings, notes, refusals, exit statuses, generated plans, and Git failure reporting without tracebacks.
tests/test_adopt_c_locale_bytes.py

Assessment against linked issues

Issue Objective Addressed Explanation
#653 Prevent GitHub-mode execute from failing under LC_ALL=C PYTHONUTF8=0 by passing the leg description and pull-request body to gh as their intended UTF-8 bytes. ✅
#653 Allow plan to run git grep with non-ASCII spec-directory names and code paths under the C locale, so it can produce the plan and cross-leg findings. ✅
#653 Allow check and execute to resolve non-ASCII submodule URLs and related repository paths under the C locale without UnicodeEncodeError failures. ✅

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Brett Heap and others added 3 commits October 16, 2026 17:41
…s asked of a UTF-8 branch name and of one holding a byte that is not UTF-8, present and missing, under `LC_ALL=C PYTHONUTF8=0`, so a strict encode or a bytes argument in `git_out`'s refusal would fail a test (#653)

#425's review named two risks for 04c6843's `_git_out_exact`. Both hold on 04c6843, and this test now holds them.

- The name must be encoded with `surrogateescape`: #425 keeps a branch byte that is not UTF-8 as a lone surrogate (`caf\udce9`), in every locale. `_git_out_exact` hands bytes only through `_path_argument` (UTF-8, `surrogateescape`), and where it hands `git_out` text, `subprocess` encodes it with `os.fsencode`, which uses `surrogateescape` too.
- No bytes may reach a refusal that joins the arguments as text, as `git_out`'s does. `_git_out_exact` joins the arguments as the text they were given, and hands `git_out` only text.

The test runs `_git_out_exact(["rev-parse", <name>], ...)` in a child under the C locale, for `café` and `caf\udce9`, each present and missing. A present branch gives back its commit. A missing one is refused `git-failed`, naming `git rev-parse <name>`. With the helper encoding strictly, the two `caf\udce9` cases fail with `UnicodeEncodeError`. With it handing `git_out` bytes, the two missing cases fail with `TypeError: sequence item 0: expected str instance, bytes found`. adopt-project.py is unchanged.

Lane: openreposhape-1 (openRepoShape-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…own words since #634, the folder named with `shown_path` and git's message `spelled` as one line, so under `LC_ALL=C PYTHONUTF8=0` a refused `git rev-parse café` no longer reads otherwise than in UTF-8 mode, nor begins a line from a folder's name (#653)

r653's review of cc652b1, F1. #634 (#610, 30676c9) changed the refusal of `git_out` in scripts/repo_shape.py: it now names the folder with `shown_path` and spells what git printed as one line. `_git_out_exact` copies that refusal for the path it runs itself, which is the C locale with a name outside ASCII, and it still built the old text: the folder and git's lines raw. On any merge with main, `test_a_default_branch_outside_ascii_reads_as_in_utf_8_mode[gone]` failed in both modes. And under the C locale, a folder named with a line break began a line the tool never printed, the one #610 closed for `git_out`.

- `shown_path` and `spelled` (as `spelled_as_git_out`, because adopt-project.py has local variables named `spelled`) are imported from the pinned module. `_git_out_exact`'s refusal uses them exactly as `git_out` does. The docstring says the two must stay in step.
- `test_git_out_exact_refuses_in_git_out_s_own_words` asks `git_out` and `_git_out_exact` the same failing command in a folder whose name `shown_path` spells (U+2028), with git's message three lines long. The two refusals must be equal, on one line. With `shown_path` reverted alone, it fails. With `spelled` reverted alone, it fails, and so does `[gone]`.

Main (b618ec1) was merged into the branch first, so this head holds #634.

Lane: openreposhape-1 (openRepoShape-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@brettheap
brettheap marked this pull request as ready for review October 16, 2026 18:23
@sourcery-ai

sourcery-ai Bot commented Oct 16, 2026

Copy link
Copy Markdown

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 hours and 50 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@sonarqubecloud

Copy link
Copy Markdown

@brettheap
brettheap merged commit e634d29 into main Oct 16, 2026
5 checks passed
@brettheap
brettheap deleted the fix/653-adopt-c-locale-encoding branch October 16, 2026 19:48
brettheap pushed a commit that referenced this pull request Oct 17, 2026
main gained 25 pull requests since de92614: #634, #699, #671, #719,
#701, #717, #700, #702, #722, #720, #725, #721, #728, #705, #727, #718,
#744, #743, #749, #740, #737, #734, #742, #751 and #736. No conflict:
AGENTS.md, adopt-project.py and docs/cli.md merged as text, and
README.md and docs/handbook.html are unchanged by the merge. README.md
is 1473 lines, its cap; AGENTS.md 494, its cap; every handbook marker is
current; `render-cli-reference.py --check` is ok.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 17, 2026
…es every reason THIS checkout cannot answer, and the notes are current to main bf1f099 with the 25 pull requests since de92614 (#563, #683)

- W683 and RV-f's N4 on #742: README's exit-3 list (Keeping a project's
  shape current, :1208-1220) and its handbook twin (`doctor`) named only a
  pin whose commit this checkout does not carry. They now say, as
  shape-doctor.py's EXIT CODES does: a pin's commit it lacks, a partial
  clone missing what is compared (#683), a read that fails
  (`update-upstream-unreadable`), a crashed validator of its own (#278,
  #410), or its own naming policy not loading (#678). +1 line, with
  tests/readme-cap.d/683.txt; README.md is 1474, its cap (1422 + 2 + 5 +
  12 + 22 + 10 + 1). The two `Keeping a project's shape current` markers
  move: `doctor` text and marker, `current` marker only.
- The notes, "What changed since v1.0.1", one entry per pull request from
  `git log --first-parent de92614..bf1f099`, each from its squash commit and
  the issues GitHub says it closes:
  - adopt-project.py: #699, #719, #749, #727, #717, #728 (with #621's
    hint), #743 + #720 + #718, #722, #721;
  - update-shape.py, bump-leg.py and a project's own scripts: #702 (with
    the pinned validators), #634;
  - scaffold, setup, family and the shared readers: #725, #671, #744;
  - shape-doctor.py: #742, #701, #737, #751, #736, #734, #700 + #705,
    #740;
  - install, CI and the docs: the exit-3 sentence above.
- Counts (99 pull requests since bfed143; 109 of 135 milestone issues
  closed at 10:28Z), the "current to main" sha and the open list (26) are
  at main bf1f099, still placeholders for tag time.

Left for round B: lane 5's eight lines (#630 issuecomment-6227129879),
whose pull requests #726, #747, #682, #724, #746, #745 and #738 are open
drafts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 17, 2026
…d A3 (three SHOULDs, four nits), and notes entries for the seven pull requests landed since bf1f099, current to main 2000488 (#563, #683)

Round A4, from lane 2's review (#630 issuecomment-6233033833, READY):
- SHOULD 1: the doctor's exit-3 list names "a validator of its own that
  crashed or could not be run", so the case of one that cannot be given
  its `PYTHONPATH` (shape-doctor.py's EXIT CODES, #278) is listed too:
  README.md:1208-1222, its handbook twin (`doctor`) and the notes' docs
  line. The paragraph re-wraps to one more line:
  tests/readme-cap.d/683.txt is +2, and its text now says which causes
  the EXIT CODES block names and that #742 added the partial clone and
  #701 the naming policy. README.md is 1475 lines, its cap (1422 + 2 + 5
  + 12 + 22 + 10 + 2). Both `Keeping a project's shape current` markers
  move to c728c970: `doctor` text and marker, `current` marker only.
- SHOULD 2: #727's entry names the leg description, whose em dash stopped
  every `execute` against GitHub at the first leg.
- SHOULD 3: #749's entry names `source-ambiguous`, for an `org/repo` that
  is also a directory here.
- Nits: the doctor section's three near-duplicate pairs are merged (#636
  with #737, #632 with #700 and #705, #586 with #736); #718 has its own
  bullet; #634's entry names `git_out`'s spelling for every caller and
  #744's the gate's `--link-source-label` and `--link-remedy`; the header
  comment says 2026-10-17 at main 2000488.

Wave B1, `git log --first-parent bf1f099..2000488`: #706 (#664), #703
(#696), #670 (#646), #682 (#648), #726 (#667), #730 (#666) and #724
(#665), each from its squash commit; #726's and #682's from lane 5's lines
(#630 issuecomment-6227129879), #682's with the unmerged `.gitmodules`
its last round added, and #724's with lane 5's note that v1.0.0's
test-hygiene line on the generator's `.pyc` files closes. The counts (106
pull requests; 116 of 135 milestone issues closed at 11:53Z), "current to
main" and the open list (19) are at main 2000488, still placeholders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant