Repository navigation
render-cli-reference.py and tests/: the CLI reference's children write no bytecode under templates/, the launcher guard reads family.py's siblings child, the quoting-sentence inventory follows an aliased quoter, a suffixless tool and each tool's calling functions, and an S5778 pytest.raises builds its argument first (#665) - #724
Conversation
…e no bytecode under templates/, the launcher guard reads family.py's siblings child, the quoting-sentence inventory follows an aliased quoter, a suffixless tool and each tool's calling functions, and an S5778 pytest.raises builds its argument first, so each guard fails when what it names breaks (#665) Four guards held less than they said. - scripts/render-cli-reference.py: `child_env()` sets PYTHONDONTWRITEBYTECODE=1, so the template's `siblings.py` (`from bootstrap import ...` from its own directory) leaves no `bootstrap.cpython-312.pyc` under templates/family-root/scripts/ when the generator runs its `--help` children. No `--help` text changes, so docs/cli.md is not regenerated (`render-cli-reference.py --check` still says it is what the generator produces). tests/test_cli_reference.py holds the variable in the environment and holds that an interpreter started under it writes none. - tests/test_validator_child_encoding.py: `interpreter_runs` accepts `[sys.executable or PYTHON, ...]` as well as `[sys.executable, ...]`, and LAUNCHERS gains scripts/family.py with `inherited_child_environment`, so dropping `env=` from `family.py siblings`' child (#214) fails the guard. - tests/test_readme_windows_quoting_sentence.py: the inventory follows `from repo_shape import shell_line as joined` (and `joined = shell_line`), scans every file `git ls-files` lists that is a `.py` file or has a python shebang (the root `bootstrap`), pins the functions that call a host-dialect quoter for scripts/family.py and setup-project.py instead of the file (shape-doctor.py stays every line, as the sentence says), and exempts the bodies of `shell_quoted` and `shell_line` in scripts/repo_shape.py by name, not the whole file. No quoting changes: the Git Bash dialect is a separate issue. - tests/test_bump_leg_paste_lines.py: `leg_placement(leg)` is evaluated before the `with pytest.raises(LegNotAsFound)` in `test_pasting_the_status_line_reads_that_leg` (Sonar S5778), as the test above it does. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-5, 2026-10-16T14:23Z) @codex review |
Reviewer's GuideThe PR closes four coverage gaps in repository guards: it makes CLI-reference generation bytecode-free, enforces inherited child environments for Sequence diagram for bytecode-free CLI reference generationsequenceDiagram
participant Generator as render-cli-reference.py
participant Child as siblings.py --help
participant Filesystem
Generator->>Generator: child_env()
Generator->>Child: subprocess.run(..., env=child_env())
Child->>Filesystem: import bootstrap
Filesystem-->>Child: module loaded
Note over Child,Filesystem: PYTHONDONTWRITEBYTECODE=1
Child-->>Generator: help text
Generator-->>Filesystem: no __pycache__ or .pyc written
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 151a0861dd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| cwd=str(REPO), capture_output=True, check=True).stdout | ||
| sources = {} | ||
| for raw in sorted(set(listed.split(b"\0"))): | ||
| relative = raw.decode("utf-8") |
There was a problem hiding this comment.
Decode Git paths with filesystem semantics
On POSIX, git ls-files -z returns raw filename bytes, so a tracked or untracked path containing a non-UTF-8 byte causes raw.decode("utf-8") to raise before the code can even determine whether the file is Python source. The previous Path.rglob approach preserved such names through Python's filesystem decoding, and this repository explicitly supports Git paths with non-UTF-8 bytes. Use os.fsdecode(raw) or UTF-8 with surrogateescape so an unrelated filename cannot break this test.
Useful? React with 👍 / 👎.
| calling = set(host_quoting(tool_sources()).get(tool, {})) | ||
| assert calling == NAMED[tool], ( |
There was a problem hiding this comment.
Pin individual host-quoting calls instead of function names
Reducing host_quoting() to its set of function keys makes this guard unable to detect a new or changed command inside an already-approved function. This already masks the cd {shell_quoted(work)} line in family.py::_print_next_steps: the README enumerates that tool's NEXT, switch -c, add, and git rm -r --cached lines, but the test passes because _print_next_steps also contains the approved switch -c call. Comparing the individual calls or command-producing helpers is necessary for the new guard to enforce the sentence it claims to protect.
Useful? React with 👍 / 👎.
…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>
|
|
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 12 hours and 5 minutes by commenting |
main gained #706 (#664), #703 (#696), #670 (#646), #682 (#648), #726 (#667), #730 (#666) and #724 (#665) since bf1f099. No conflict: #682's README sentence and its handbook twin (Families) merged as text, with #682's own Families marker, and every marker reads current. README.md is 1474 lines, its cap; AGENTS.md 494, its cap; `render-cli-reference.py --check` is ok. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…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>
Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…alue and pins each function's host-dialect calls, and the launcher guard reads `run_child` and `run([PYTHON, ...])`, with rows for `cli.py` and `setup-project.py` (#759) (#808) **Based on main.** #724 landed as `20004887`; this branch holds it and was restacked onto main as `84ce67c9`. **Head `08a6ed39`.** The branch holds #759's commit `052410b6`, two merges that took #724's final head and main, and one fix round (`08a6ed39`, lane 2's rv808). Tests only: `tests/test_readme_windows_quoting_sentence.py` and `tests/test_validator_child_encoding.py`. No shipped file changes, and no pinned copy. README, the handbook, `AGENTS.md` and `docs/cli.md` are untouched, and `render-cli-reference.py --check` passes. One commit, tests only: `tests/test_validator_child_encoding.py` and `tests/test_readme_windows_quoting_sentence.py`. No shipped file changes, so no pinned copy, README, handbook, `AGENTS.md` or `docs/cli.md` text moves. Pushed to `fix/759-quoting-launcher-guards` as a fast-forward of `84ce67c9`, the coordinator's restack onto main (which answers MUST 1, the conflict with main). Nothing was merged, rebased or amended. ### SHOULD 1: the `setup-project.py` row now flips on the fix it names, and the finding says six children **What failed, measured.** On `84ce67c9`, with the real `setup-project.py` changed in a scratch clone, one change per row. The row is `test_the_tools_that_run_the_projects_validators_give_them_an_environment[setup-project.py]`: `xfailed` means it is still red, `FAILED (XPASS strict)` means it says the finding is fixed. Log: `v116/f808/toolmut/`. | change to `setup-project.py` | `84ce67c9`'s guard | the new guard | |---|---|---| | none (control) | xfailed | xfailed | | `env=child_environment()` in `run`'s own `subprocess.run`, which reaches all six children | **xfailed: no signal, and `NOT_YET`'s reason stays false** | FAILED (XPASS strict): the row comes out | | an `env` parameter on `run`, passed at all six calls | FAILED (XPASS strict) | FAILED (XPASS strict) | | the same, but the call in `why_the_plan_did_not_go_through` left out | **FAILED (XPASS strict), with a child still bare** | xfailed | | `env=` at the five calls the old guard read, no parameter | **FAILED (XPASS strict), with a child still bare** | xfailed | | `env=` at four of the calls | xfailed | xfailed | | a seventh bare child added | xfailed | xfailed, and `test_setup_project_starts_six_children_and_the_guard_reads_each` fails | Lane 2's two mutants are the bold rows. The first is (a): the guard read the call sites and never `run`. The second is (b): there are six Python children, not five. `why_the_plan_did_not_go_through` runs the plan again, `again = run(plan, cwd=shape_root, capture=True)`, and `plan` is a parameter, so the old `argv_of` could not read the list. **The sixth child, measured.** I called the real `why_the_plan_did_not_go_through` with a plan that prints a `project ...` line holding an ellipsis and exits 2, under three encodings (`v116/f808/probe6.txt`): | `PYTHONIOENCODING` | what the second run's captured stdout holds | the words returned | |---|---|---| | `utf-8:strict` | `project Sample ` and U+2026 | `the plan was refused (see above).` | | `cp1252:strict` | `project Sample ` and U+FFFD | the same | | `cp1252:surrogateescape` | `project Sample ` and U+FFFD | the same | So the first five children write into the stream this process reconfigured to UTF-8, which is the two-encodings finding above. The sixth is captured and read as UTF-8 whatever it wrote: a cp1252 byte becomes U+FFFD. Nothing shows today, because the only test on those lines is `startswith(PLAN_STARTS)` and that prefix is ASCII. It is the same defect class, and the fix has to reach it too. **The fix** (`tests/test_validator_child_encoding.py`): - `interpreter_runs` reads a list a caller hands a function as a parameter, one level up, and a name bound in each branch of an `if`. A starter's own parameter is left to its callers, which are the runs, so `run`'s own `subprocess.run` is not counted as a seventh. On the shipped files this changes exactly two reads (`v116/f808/probe/survey.py`): `setup-project.py` gains the sixth child (`:2312` on `84ce67c9`), and `scripts/render-cli-reference.py` gains `:403` (NIT 1). - `environment_of` reads, where a call hands no `env=`, the `env=` of the function it calls when this module defines that function (`run`). A name is read as the LAST thing its function bound it to above the call. - `NOT_YET` names the six children, says five write into the reconfigured stdout and the sixth is captured, and names both fixes the row reads: an `env=` in `run`'s own `subprocess.run`, or an `env` parameter that all six calls pass. - `test_the_row_flips_on_the_change_the_finding_names` runs seven changes to a model of `setup-project.py` (the table's rows, on a source string). `test_setup_project_starts_six_children_and_the_guard_reads_each` pins the six functions of the real file: `check_names`, `clone_and_bootstrap`, `plan_and_confirm`, `scaffold`, `self_bootstrap` and `why_the_plan_did_not_go_through`. While the row is an `xfail`, a seventh bare child changes nothing in it, so the count is what would show it. ### NITs 1 to 6: which are taken, which stay | NIT | outcome | |---|---| | **1** `argv_of` read only the first list | **Taken.** Any list the name is bound to above the call counts; the first-or-last choice is gone. `SPELLINGS` has the name bound to two lists in each order (the `render-cli-reference.py` shape), and `NOT_RUNS` has a name bound to the interpreter only after the call. | | **2** three `host_values` behaviours without a case | **Taken.** `VALUE_USES` gains `self.quote = shell_line`, `a = b = shell_quoted` and a name read in an annotation; the value use in a method sits beside `test_a_method_is_named_with_its_class`. | | **3** `environment_of` joined every binding | **Taken.** The last binding above the call is read, with cases for a later rebinding, a rebinding after a branch, and one after the call. | | **4** spellings the launcher guard does not read | **Taken in part.** `interpreter_runs`' docstring lists them, as #821's guard lists "WHAT IT DOES NOT SEE", and `SPELLINGS` gains `subprocess.call`, `check_call` and `check_output`. **Stays unread:** a tuple argv, `[sys.executable, ...] + rest`, a function-local `exe = sys.executable`, `import subprocess as sp` and `from subprocess import Popen`, `os.exec*`. None is in shipped code, and each needs a resolver of its own. | | **5** aliases counted at the alias | **Taken in part.** `alias_of` follows `q: object = shell_quoted` as it follows `q = shell_quoted`, so `q(x) + q(y)` counts 2 and one switched to `'posix'` counts 1. `names_posix` reads a `'posix'` argument only after the value the quoter would quote, so `functools.partial(quote_arg, 'posix')`, which binds the value, is a host use. **Stays unfollowed:** an alias made by a tuple, a chained assignment, a walrus or a conditional. `host_calls`' docstring says so, and `NOT_FOLLOWED` pins what is counted instead (the statement, once for each quoter it names). None is in shipped code. | | **6** #821's `calls_to` reads `host_call` only | **Not changed here, and not #821.** See below. | **NIT 6, for whichever of #808 and #821 lands second.** #821's `calls_to` counts `host_call`'s calls and not `host_values`' uses, so on the merge `skips_the_question` does not see a quoter handed on as a value. Measured on a scratch merge of `08a6ed39` and #821's head `7581ffb9` (`v116/f808/nit6.txt`): | source | `skips_the_question("scripts/x.py", ...)` as merged | with the fix below | |---|---|---| | `' '.join(map(shell_quoted, w)) if os.name == 'nt' else ''` in `def a(w)` | `set()` | `{'a'}` | | `' '.join(shell_quoted(x) for x in w) if os.name == 'nt' else ''` in `def a(w)` | `{'a'}` | `{'a'}` | The fix is one expression, which the second lander adds to `calls_to`'s return: `| {function for function, _ in host_values(tree, names)}`. `host_values` names functions as `calls_to` does (`<module>`, `Class.method`). With it, `tests/test_readme_windows_quoting_sentence.py` on the scratch merge gives 43 passed (`v116/f808/nit6-run.log`). The merge itself is clean and needs no other change. ### Tests On `08a6ed39` (`dirty=0`), through `run9.sh` (gh stub first on PATH, bogus token, empty `GH_CONFIG_DIR`), `v116/f808/final-2.log`: `tests/test_validator_child_encoding.py tests/test_readme_windows_quoting_sentence.py tests/test_repo_hygiene.py tests/test_agents_md_codes.py tests/test_cli_reference.py`: **297 passed, 1 xfailed in 59.36s.** Per file: `test_validator_child_encoding.py` 82 to 111 cases (29 new), `test_readme_windows_quoting_sentence.py` 28 to 40 (12 new), the other three 147 together. On `84ce67c9` the two files gave 109 passed, 1 xfailed (`v116/f808/base-head.log`). No other test file imports or drives either one: `git grep` finds no use of `interpreter_runs`, `environment_of`, `LAUNCHERS`, `host_calls`, `host_quoting` or `quoter_names` outside these two files. On the scratch merge with #821 (`4386e5fe`, not pushed), these two files and `test_git_bash_dialect.py`, `test_family_printed_quoting.py` and `test_pinned_copy_text.py` give 417 passed, 1 xfailed (`v116/f808/merge821.log`). ### The new tests on `84ce67c9` I put the new test files over `84ce67c9` with its helpers back in place of the new ones (`v116/f808/mkbase.py`; a helper that is new is dropped, so a test that uses it fails with a `NameError`): **19 of the 41 new tests fail, 131 pass, 1 xfails** (`v116/f808/basefail-3.log`). The failures: 7 changes to the model of `setup-project.py` and the six-children pin; 3 `SPELLINGS` (a list a caller hands a parameter, by position and by keyword, and a name bound to two lists with the interpreter second); 5 `ENVIRONMENTS` (the last binding, three ways, and the environment `run` hands, two ways); and in the quoting file the `partial(quote_arg, 'posix')` use, the annotated alias statement, and the annotated alias counted at its calls. The other 22 pass there, because the base code already did what they say. They close mutants that survive `84ce67c9`'s own tests, measured below (six of them: NIT 2's three and NIT 4's three), or pin a limit the new code names. ### Mutants One mutant per new hunk, each a text change to the head's test files (`v116/f808/mutate.py`, `v116/f808/mut/results.jsonl`). `failed` counts the tests that fail, among the launcher guard's own tests for a mutant of `tests/test_validator_child_encoding.py` and in the whole file for one of `tests/test_readme_windows_quoting_sentence.py`. All 32 are caught. | hunk | mutant | failed | |---|---|---| | `bound_values`, above the call | the filter dropped / line number only / no sort | 2 / 1 / 1 | | `environment_of`, last binding | the first binding / every binding joined (the old code) | 2 / 2 | | `argument_for` | positional dropped / keyword dropped / always the first argument | 9 / 1 / 1 | | `lists_of`, callers | not read / callers of callers read / a starter's own parameter resolved | 10 / 1 / 15 | | `lists_of`, held lists | first only / last only | 1 / 1 | | `interpreter_runs`, any list | first only / last only | 1 / 1 | | `own_environment_of` | returns nothing / the call's own `env=` loses to it / recurses into a starter that calls itself | 3 / 4 / 1 | | `NOT_YET` row | unmarked / made to pass (`LAUNCHERS` value `""`, the strict `xfail` fires) | 1 / 1 | | `alias_of` | no annotated assignment / chained assignment read / any target kind read | 2 / 1 / 1 | | `names_posix` | every argument read / one slot after the quoter / a keyword quoter reads none | 1 / 1 / 1 | | `host_values`, alias statement | any child of one exempt / the statement a use | 1 / 5 | | `function_of` | `ClassDef` dropped | 1 | | `STARTERS` | `subprocess.call` / `check_call` / `check_output` dropped | 1 / 1 / 1 | Six of these survive `84ce67c9`'s tests, run on its own files with the same text change: `STARTERS` (three; 28 passed, 1 xfailed in the launcher guard's tests) and, in the quoting file (28 passed each), `host_values`' target-count check (`a = b = shell_quoted`), its target-kind check (`self.quote = shell_line`) and `function_of`'s `ClassDef`. On the new files each is caught, as the table says. ### Merge checks (`git merge-tree --write-tree HEAD <ref>`, main at `64ec47c3`) - **origin/main:** clean. - **#821 (`fix/711-git-bash-dialect`, `7581ffb9`):** clean, and the only other head that changes `tests/test_readme_windows_quoting_sentence.py`. This branch alone changes `tests/test_validator_child_encoding.py`. - **Every `fix/*` and `l3-*` head on origin (59, this branch's own among them):** 55 clean. Four conflict: `fix/758-git-command-scope`, `fix/762-validate-family-remedies-texts`, `l3-677-pins-hand-step-wip` and `l3-678-naming-checkout-wip`. Each conflicts the same way with origin/main and with `84ce67c9`, in files this change does not touch. ### Checklist candidates None new. NIT 6 is the one follow-up, and it belongs to whichever of #808 and #821 lands second. **1. The inventory sees a quoter handed on as a value, and counts calls.** `tests/test_readme_windows_quoting_sentence.py`: - `host_calls` also returns a host quoter that is read and not called: an argument or keyword of another call (`map(shell_quoted, words)`, `functools.partial(shell_line)`, `key=shell_quoted`), a list entry, a default or a return value. It is named under its own function (`host_quoting` keys by function), in source order. - A call told `'posix'` is `sh`'s, as for a direct call: `functools.partial(shell_line, platform="posix")` is not a use. - `NAMED` maps each function to its number of host-dialect uses (`_add_command` 3, `_print_next_steps` 2, `family_next_command` 2, the other three 1), and the per-function pin compares the counts. One of `_add_command`'s three calls switched to `'posix'` is 3 to 2 and fails, where the set of names it compared before still held `_add_command`. The failure prints the counts it found next to the table's. **2. The launcher guard reads two more spellings and has two more rows.** `tests/test_validator_child_encoding.py`: - `interpreter_runs` reads a call to `subprocess.run` and its kin, to the package's `run_child`, and to `run` (`setup-project.py`'s wrapper), whose argv is `[sys.executable, ...]`, `[sys.executable or PYTHON, ...]`, or a list starting with a name the module binds to `sys.executable` (`PYTHON = sys.executable`). The list may be spelled out, held in a local name (`plan = [PYTHON, ...]`, once or in each branch of an `if`), or handed to the calling function as a parameter by a caller in the module. A function that is itself a starter (`run`) is not a run; its callers are. A `PYTHON` imported from `repo_shape` is `python3` as a person types it, and is not read as the interpreter. - `environment_of` reads an `env=` keyword, or `run_child`'s second argument, through a local name (`cli.py`'s `scaffold` builds `env` and adds a key before it passes it on). Where the call hands none, it reads the `env=` of the function it calls when the module defines that function (`run`). A local name is read as its LAST binding above the call. - `LAUNCHERS` gets `src/openreposhape/cli.py`: its four `run_child` calls (`:436`, `:462`, `:474`, `:564`) each hand `standard.child_environment()`, which is what the file's docstring promises ("EVERY CHILD GETS `standard.child_environment()`"). That is the package environment's PATH and not an encoding, so it is the helper this row is held to. The row passes. - `LAUNCHERS` gets `setup-project.py`: its six Python children hand no environment, since `run` has no parameter for one: five `run([PYTHON, ...])` calls (in `self_bootstrap`, `check_names`, `plan_and_confirm`, `scaffold` and `clone_and_bootstrap`) and the sixth, in `why_the_plan_did_not_go_through`, which runs the plan again with `plan` a parameter. That row fails, and it is the finding below. - A separate test says each row finds at least one run. That keeps a spelling the guard cannot read from passing as "nothing to check". **Measured, not read.** `PYTHONIOENCODING=cp1252:strict` and `cp1252:surrogateescape` are what the other encoding tests here emulate for Windows' ANSI code page on a stdout that is not the console. A local run, `setup-project.py --project Sample --yes --org demoorg --local-remote-dir <dir> --into <dir>`, with the `gh` stub first on PATH: | `PYTHONIOENCODING` | exit | lines of stdout | lines that are not valid UTF-8 | |---|---|---|---| | `cp1252:strict` | 0 | 129 | 9 (24, 31, 38, 45, 64, 79, 80, 100, 102) | | `cp1252:surrogateescape` | 0 | 129 | 9 (the same) | | `utf-8:strict` (control) | 0 | 129 | 0 | `setup-project.py` reconfigures its own stdout to UTF-8 (`main`, `:2790`), and five of its children inherit that stdout with an environment of their own, so each writes in the code page its environment gives, and the sixth is captured and read as UTF-8 whatever it wrote (the table under "The sixth child, measured"). The lines are the naming validator's `family ...` rows with their em dash (3 lines), the scaffold's `shape ...` and `spec`/`code` lines with their ellipsis (4), and the validators `scripts/bootstrap.py` runs (2). Nothing raises, because `scaffold-project.py` and `scripts/bootstrap.py` escape what their stdout cannot hold. The stream is just two encodings. Log: `v115/w759/probe5/summary.txt`. **What the test does about it.** The row cannot pass without a change to `setup-project.py`, and this issue changes no tool. It is a strict `xfail` (`NOT_YET`): it passes while the row fails, and it fails the day the row passes, so whoever gives `run` an environment has to take the row out of the table. The mark names `AssertionError`, so any other error in the guard still fails. The fix is an environment from a copy of `inherited_child_environment` (reading `sys.stdout.encoding` after the reconfigure, so `utf-8:replace`), given either as an `env=` in `run`'s own `subprocess.run` or as an `env` parameter that all six calls pass. The row flips on either, and a change that leaves any one of the six bare leaves it red. `setup-project.py` cannot import the helper: it runs before there is a checkout to import from. - **The use rule is wider than the issue's wording, by one case.** The issue says "an argument of any call, or the value of an assignment the alias walk already follows". The rule here is: any read of a host quoter that is not the `func` of a call and not the alias statement itself (`joined = shell_line`, the assignment `quoter_names` follows). That covers the issue's two forms and also a list entry, a default, a return value and `self.quote = shell_line` (an assignment the walk does not follow). It differs on one point only: an alias that is assigned and never read. The alias statement is not counted, because every use of `joined` is, and counting it would make a module-level alias a `<module>` row that `NAMED` would have to name. - **A call that takes the quoter as an argument and is told `'posix'` is not a use.** `host_values` has its own small `names_posix` rather than reusing `host_call`'s inline test. `host_call` is left as it is, since #711 (the Git Bash dialect) edits the rule that decides what forces `sh`. - **The new cases sit in the middle of the file and `NAMED` changes only in its values.** #711 changes `tests/test_readme_windows_quoting_sentence.py` too, so these hunks are additive, and none of them is at the end of the file. - **The callee set is `subprocess.run/Popen/call/check_call/check_output`, `run` and `run_child`.** A call that only names the interpreter in a list (`shlex.join([sys.executable, ...])`) is not a run. Another spelling is a new entry in `STARTERS`, and `test_every_spelling_of_a_child_of_the_interpreter_is_a_run` is where it is shown. **On the shipped tools nothing changes.** The new `host_calls` returns what the old one did on every file `git ls-files` lists (`host_quoting` compared across both: identical), so no host-dialect line escaped the sentence before, as the issue says. `NAMED`'s counts match today's code: `_add_command` 3, `_print_next_steps` 2, `_switch_command`, `_print_next` and `_checked_mount_path` 1, `family_next_command` 2. **The tool mutants,** each run against `151a0861`'s own two test files and then against these. Logs under `v115/w759/mut/`. | Mutant | base tests | new tests | |---|---|---| | M1: `def _w759_mutant(words): return list(map(shell_quoted, words))` added to `update-shape.py` | 9 passed (survives) | `test_the_tools_that_quote_for_the_host_are_the_ones_the_sentence_names` fails: `update-shape.py` is among the files | | M2: `shell_quoted(org, 'posix')` in `_add_command` (one of three) | 9 passed (survives) | `test_the_functions_that_quote_for_the_host_...[scripts/family.py]` fails: `_add_command` 2, the table 3 | | M3a-d: `env` dropped from one of `cli.py`'s four `run_child` calls (`:436`, `:462`, `:474`, `:564`), one mutant each | 3 passed (survives) | the `[src/openreposhape/cli.py]` row fails, naming the line: ``src/openreposhape/cli.py:474 runs the interpreter without `env=standard.child_environment()` `` | | M3e: `env = dict(os.environ)` for `scaffold`'s `env` | 3 passed (survives) | the same row fails | **Against the base's helpers.** The new cases appended to `151a0861`'s files, without the new helpers: 12 of 19 fail in the quoting file and 12 of 19 in the launcher file (a `NameError` where the helper is new, an assertion where it is not). The 14 that pass are the negative cases (what is not a use, what is not a run) and the two `subprocess.run` spellings the guard already read. The `cli.py` and `setup-project.py` rows have no base row to run. Logs `v115/w759/basefail-quote.log` and `basefail-enc.log`. **One mutant per new hunk,** applied to the new test files only, each killed (`failed` counts the tests that fail): | Hunk | Mutant | failed | |---|---|---| | `host_calls` adds `host_values` | the value uses left out | 11 of 28 | | `host_values`, `names_posix` call | a call told `'posix'` still a use | 4 | | `host_values`, keyword | `ast.keyword` not climbed | 1 | | `host_values`, alias statement | the alias statement a use | 3 | | `names_posix` keyword | `platform=`/`posix=` ignored | 3 | | `names_posix` argument | a `'posix'` argument ignored | 1 | | `function_of` | every use named `<module>` | 11 | | source order | no sort by line | 1 | | `host_use_counts` | a count of 1 for every function | 3 | | `STARTERS` | `run_child` left out | 6 | | `STARTERS` | `run` left out | 6 | | `interpreter_names` | a name bound to `sys.executable` not followed | 6 | | `argv_of` (now `lists_of` and `argv_lists`; the fix round's own mutants table covers them) | a list held in a name not resolved | 1 | | `environment_of`, `run_child` | its second argument ignored | 4 | | `environment_of`, local name | a name not read as what it was bound to | 2 | | `NOT_YET` | the `xfail` mark dropped (the row is red) | 1 | | `NOT_YET` | the row made to pass (`LAUNCHERS` value `""`): the strict `xfail` fails | 1 | **These counts are `052410b6`'s.** At `08a6ed39` they are the fix round's: 297 passed, 1 xfailed over the same five files; the encoding file grows from 82 to 111 cases and the quoting file from 28 to 40. On `052410b6`, `run9.sh` (gh stub first on PATH, `GH_TOKEN` bogus, empty `GH_CONFIG_DIR`; `v115/w759/final-2.log`): `tests/test_validator_child_encoding.py tests/test_readme_windows_quoting_sentence.py tests/test_repo_hygiene.py tests/test_agents_md_codes.py tests/test_cli_reference.py`: **256 passed, 1 xfailed in 63.09s.** Per file: `test_readme_windows_quoting_sentence.py` 9 to 28 cases (19 new), `test_validator_child_encoding.py` 56 to 82 (26 new, one of them the `xfail` row), the other three 147 together. No other test file imports or drives what changed: `git grep` finds no use of `interpreter_runs`, `LAUNCHERS`, `host_calls` or `host_quoting` outside these two files. - **origin/main `64ec47c3`:** clean. **#724** landed as `20004887`, and nothing is stacked. The fix round's merge checks above are at `08a6ed39`; the rest of this section is `052410b6`'s. - **Every `fix/*` and `l3-*` head on origin (47):** clean, and none other than #724 touches the two test files. The branches that touch a tool the guards read (`scripts/family.py`: `fix/646`, `fix/684`, `fix/685`, `fix/686`, `fix/688`, `fix/689`, `fix/692`, `fix/693`; `update-shape.py`: `fix/674`; `setup-project.py`: `fix/696`, `fix/729`) are clean too. - **The wave-7 branches' committed tips** (`fix/757` to `fix/777`; `fix/760` is on origin): clean. - **Lane 2's #711 (the Git Bash dialect):** no branch on origin at 10:52Z (`git ls-remote origin 'refs/heads/*711*' 'refs/pull/711/head'` is empty), so it is not checked. It changes `NAMED` and this file; whichever lands second re-gates against the other. - Three older local branches (`fix/bootstrap-fast-forwards-tracking-branch`, `fix/scaffold-mount-ignored-legs`, `fix/scaffold-reuse-seeded-legs`) conflict, in files this does not touch, exactly as they do with origin/main. - **No change to a tool.** The `setup-project.py` row is the finding, held as a strict `xfail`; fixing `run` is a separate change. - **Dynamic access is not seen.** `getattr(repo_shape, "shell_line")` and `globals()["shell_line"]` hand a quoter on by name at run time. The inventory reads names in the source. - **The guard still reads only the tools in `LAUNCHERS`.** A survey with the new `interpreter_runs` over every shipped file found five more: `shape-doctor.py:1697` (sets its own UTF-8, as the table's comment says), `src/openreposhape/standard.py:560` (a digest program with captured output), `scripts/render-cli-reference.py:403` (passes `env=child_env()`, found once NIT 1 was fixed), `templates/assembly-root/scripts/bootstrap.py:148` and `templates/family-root/scripts/siblings.py:567` (neither passes an environment; see below). - **`templates/assembly-root/scripts/bootstrap.py`'s `_run` (`:148`) starts the validators with no environment, so a manifest name the code page cannot hold ends bootstrap.** Measured on a scaffolded project (a local `setup-project.py` run, then `project.yaml` `name: "日本"`), `PYTHONIOENCODING=cp1252:strict python3 scripts/bootstrap.py`: `validate-manifest.py` raises `UnicodeEncodeError` at `print(f"manifest ok: {manifest.get('name')} ...")` (`:972`) and bootstrap ends `bootstrap FAILED: manifest (exit 1)`, exit 1. With `utf-8:strict`: `manifest ok: 日本 (sample), 3 legs`, exit 0. It is #254's defect in a pinned copy, so a fix is a shape bump, and `make bootstrap` and `setup-project.py` step (7) both run it. - **`setup-project.py`'s `run` gives its children no environment** (the finding above). If the coordinator files it, the `NOT_YET` entry names the fix and the measurement. - **`templates/family-root/scripts/siblings.py:567`** starts each member's own `scripts/bootstrap.py` with no environment. Its stdout and the child's agree (neither reconfigures), so it is the first candidate's class and not the second's, and it has no row because it is a pinned copy. Review: lane 2's independent review (issuecomment-6236457891) and its re-verification of 08a6ed3, READY (issuecomment-6237256937). CI (Brett Heap's ruling 27): H0 is the head 08a6ed3 itself, which passed tests, tests-git-bash and SonarCloud; main has touched none of its files since its base 40e678e; and the local gate passed on its predicted squash tree 817a2ff over main 2c6543e: the readme_windows_quoting_sentence, validator_child_encoding, repo_hygiene, agents_md_codes, cli_reference sets, 298 passed, 1 xfailed. Landed on Brett Heap's word of 2026-10-17T21:47:28Z, in lane 5's session (posted on #473 as issuecomment-6241758069): "merge lane 5's v1.1.1 PRs once reviewed and green". Closes #759 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…book: under Git Bash (`MSYSTEM` set) the lines that follow the host are quoted for `sh`, through one question, `host_shell_dialect`, so the doctor's lines, setup's family lines and `family.py`'s `NEXT`, `switch -c`, `add` and `cd` lines paste whole in the shell that printed them, and README's Windows sentence says so (#711) (#821) The tools that quote a printed line for the host chose PowerShell's dialect wherever `os.name` was `nt`, and it is `nt` under Git Bash too, which runs the Windows Python. So Git Bash was handed `'C:\Users\Jane O''Neill'`, which it reads as `C:\Users\Jane ONeill`, a bare `C:\work\Atlas`, which it reads as `C:workAtlas`, and `Copy-Item` and `$env:PYTHONPATH = ...;`, which it has no command for. Brett Heap chose option (a) of #532's Git Bash question at 2026-10-16T13:07:34Z. - scripts/repo_shape.py: `host_shell_dialect(name=None, environ=None)` answers "nt" (PowerShell) on a Windows host whose environment has no `MSYSTEM`, or an empty one, and "posix" (`sh`) everywhere else, Git Bash and every MSYS2 shell included. `shell_quoted` and `shell_line` ask it when they are given no `platform`; an explicit one keeps its meaning. Its docstring names the limit: a PowerShell started from inside Git Bash inherits `MSYSTEM` and is answered `sh`'s. - setup-project.py carries a copy, as it does of the quoting, because it runs before any checkout of the standard exists. `SHELL_BARE`, decided at import from `os.name`, is gone: `shell_word` asks the question at each call and takes the alphabet and the escape from one answer, which the old code read twice. Setup's family lines follow it (L2-29). - shape-doctor.py: `quote_arg`, `restore_from_head`, `validator_command`, `bump_leg_command`, `checkout_notes`, `submodule_update_next` and `copy_command` ask it, so Git Bash gets `sh`'s quoting, `cp` and `env PYTHONPATH=...`. `scaffold_command` keeps its entry point and interpreter on the machine (`python setup-project.py` on Windows, from either shell) and takes only its quoting from the question. The interpreter is `PYTHON`, the machine's, on every line. - scripts/family.py is unchanged: its host-dialect lines go through `shell_quoted` and `shell_line`, `_print_next_steps`'s `cd` included. Tests: tests/test_git_bash_dialect.py emulates PowerShell, Git Bash, an empty `MSYSTEM` and POSIX on any host (each module under test gets an `os` whose `name` is the host's; the real `os.name` is never touched) and reads each line back with `shlex.split` or `powershell_words`: the question's answers, setup's copy held to them, `shell_quoted` and `shell_word` held to one answer on every host, and the lines of setup, family.py and the doctor. #724's inventory in tests/test_readme_windows_quoting_sentence.py now fails a function that quotes for the host and reads `os.name` itself. tests/test_family_printed_quoting.py reads the host through the question, and tests/test_setup_project_py.py unsets `MSYSTEM` where it patched the gone `SHELL_BARE`. Fix round 1 (rv711, no MUST): no pinned script may import `host_shell_dialect` (siblings.py's older-library test strips it too, and the skew test reads all seven pinned scripts that import from repo_shape); the guard counts the doctor's nine line builders, the functions that hand a `platform` on to a quoter, as quoters, so `copy_command(s, d, os.name)` in a new row fails it, and names a quoter's body by `Class.method`; the Git Bash rows read back a kept repository's line and a lane trailer spelled by the line's own quoter. A lane name cannot hold an apostrophe (`LANE_NAME_RE`), so that trailer test hands the quoter one through a stand-in. README and the handbook: README's Windows sentence keeps "Printed commands are quoted for a POSIX shell, so paste them into Git Bash", names `init`'s `cd` line among `family.py`'s, and says the exceptions are "quoted for the shell they are printed in, PowerShell or Git Bash, on Windows." #785's sentence after it says a path `shape-doctor.py` cannot print as itself is written `$'...'` "Off Windows and in Git Bash": its `quote_arg` now asks `host_shell_dialect` too, so Git Bash is handed `sh`'s spelling of such a path. The handbook's Windows panel says the same, and adds that a shell started from inside Git Bash inherits `MSYSTEM`, so a PowerShell started there is handed Git Bash's quoting, and a cmd.exe started anywhere else is handed PowerShell's. The seven markers of README's § Starting a project in a new organisation move to its new blob, e0d2b672, and tests/readme-cap.d/711.txt files the two lines this adds (README 1476 -> 1478, the cap 1477 -> 1479). NIT 6 of lane 2's review of #808, which landed first: the one-question guard's `calls_to` counts a quoter handed on as a value (`host_values`) as well as a call, so `map(shell_quoted, w)` beside a branch on `os.name` is seen. Stacked on #724, whose final head 949fd4d is merged here and which has landed as 2000488, merged here with main. #630, which rewrote the README and handbook around this sentence, has landed too (190133d), and so has #792 (#785, 5cbec22), which wrote into the same paragraph and into `quote_arg`; main d7befb9 is merged here with both. The copied `scripts/repo_shape.py` changes, so it rides in v1.1.1's shape bump. Closes #711 Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-5 (openRepoShape-5)
Claim: #665 (comment)
Four guards held less than they said. One commit,
151a0861, on8062810d. No shipped behaviour changes except one environment variable in the generator's--helpchildren. No--helptext changes, sodocs/cli.mdis not regenerated. No quoting changes.What changed
scripts/render-cli-reference.py:child_env()setsPYTHONDONTWRITEBYTECODE=1. The template'ssiblings.pydoesfrom bootstrap import ...from its own directory, so the generator's--helpchild wrotetemplates/family-root/scripts/__pycache__/bootstrap.cpython-312.pyc(ignored by git, so silent). A person who runs the generator now gets the same tree as the test that runs it. The docstring says why.tests/test_cli_reference.pygets one test: the variable is inchild_env()whatever the caller had, and an interpreter started under that environment reportssys.dont_write_bytecodeas true.tests/test_validator_child_encoding.py: the launcher guard seesfamily.py.interpreter_runsaccepts[sys.executable or PYTHON, ...]as well as[sys.executable, ...], andLAUNCHERSgains"scripts/family.py": "inherited_child_environment". Droppingenv=fromfamily.py siblings' child (adopt-project.pyplanraisesUnicodeEncodeErrorin its terminal report, after the plan file is written, for a printable name stdout cannot encode (日本.mdunder cp1252, as on a redirected Windows stdout), soplanexits 1 beforeplan written to#214) now fails the guard.tests/test_readme_windows_quoting_sentence.py: the inventory holds what it claims. It followsfrom repo_shape import shell_line as joined(andjoined = shell_line, however many steps). It scans every filegit ls-fileslists (tracked, or new and not ignored) that is a.pyfile or starts with apythonshebang, which adds the rootbootstrap. It pins functions, not files, for the two tools the sentence names by particular lines:scripts/family.py(_print_next,_switch_command,_add_command,_print_next_steps,_checked_mount_path) andsetup-project.py(family_next_command).shape-doctor.pystays every line, because the sentence says "shape-doctor.py's lines". Only the bodies ofshell_quotedandshell_lineinscripts/repo_shape.pyare exempt, by name; the rest of that file is not. Nothing about quoting changes: the Git Bash dialect (option (a)) is its own v1.1.1 issue, and when it movesNAMED, this table moves with it.tests/test_bump_leg_paste_lines.py: Sonar S5778.placement = bump_leg_module.leg_placement(leg)before thewith pytest.raises(LegNotAsFound)intest_pasting_the_status_line_reads_that_leg, as the test above it does. Five lines; the hunk is :401-405, away from bump-leg.py and setup-project.py: thegit switch -cline says under it what to do where an earlier act left that branch, soalready existsis no dead end (#607's sentence, in the two tools it left) #657's hunks.Evidence
The reproduction (
.pyc). In a fresh shared clone at8062810d, withPYTHONDONTWRITEBYTECODEunset,python3 scripts/render-cli-reference.py --checkprinted[ok] docs/cli.md is what the generator producesand left five.pycfiles, among themtemplates/family-root/scripts/__pycache__/bootstrap.cpython-312.pycat 11533 bytes (the size the issue reports). The same command on151a0861printed the same[ok]line and left none (find . -name '*.pyc'found 0).Mutants. Each is applied to a copy at the head's test files; "old" is the same mutant under
8062810d's own tests.PYTHONDONTWRITEBYTECODEfromchild_env()(the base's behaviour)test_child_env_leaves_no_bytecode_in_the_trees_it_runsfails:KeyError: 'PYTHONDONTWRITEBYTECODE'env=fromfamily.py'ssiblingschild (scripts/family.py:6543)test_the_tools_that_run_the_projects_validators_give_them_an_environment[scripts/family.py]fails:scripts/family.py:6543 runs the interpreter without `env=inherited_child_environment()`from repo_shape import shell_line as joinedand a call, inupdate-shape.pytest_the_tools_that_quote_for_the_host_...fails: files are[family, setup-project, shape-doctor, update-shape]family.py's_switch_commandcallsshell_line(..., "posix")test_the_functions_that_quote_for_the_host_...[scripts/family.py]fails:_switch_commandmissing from the calling functionsbootstrapbootstrapis now among the filesscripts/repo_shape.pycallsshell_linescripts/repo_shape.pyis now among the filesEach new failure is one test, and the other tests of the file stay green (
1 failed, 8 passed;1 failed, 55 passed). V2, V3 and V5 are R588's mutants as the issue describes them.Tests
On
151a0861, one run,run9.sh(gh stub first on PATH,GH_TOKENbogus, emptyGH_CONFIG_DIR):tests/test_cli_reference.py tests/test_validator_child_encoding.py tests/test_readme_windows_quoting_sentence.py tests/test_bump_leg_paste_lines.py tests/test_family_siblings_placement.py tests/test_repo_hygiene.py: 227 passed in 698.59s.Per file:
test_cli_reference.py23 (one new),test_validator_child_encoding.py56 (one new case,[scripts/family.py]),test_readme_windows_quoting_sentence.py9 (6 new: thescripts/family.pyandsetup-project.pyfunction pins, the alias, the method name, the exemption, the suffixless file),test_bump_leg_paste_lines.py33,test_family_siblings_placement.pyandtest_repo_hygiene.py106 together.Merge checks (
git merge-tree --write-tree HEAD <ref>)35133f29(includes bump-leg.py and setup-project.py: thegit switch -cline says under it what to do where an earlier act left that branch, soalready existsis no dead end (#607's sentence, in the two tools it left) #657 as78fe3fff, which editstests/test_bump_leg_paste_lines.pyat :53, :235-244 andsetup-project.py): clean. On the predicted merge commit,test_readme_windows_quoting_sentence.pyandtest_validator_child_encoding.pypass (65 passed), and so doestest_bump_leg_paste_lines.py(33 passed).family_next_commandis still the only function insetup-project.pythat callsshell_word.pipx install openreposhapeis the second way in and the first on native Windows, git-filter-repo is installed in install.sh's order and never with a barepip install, and a first draft of the v1.1.0 release notes (#563) #630 (5b140bd3): conflicts indocs/handbook.htmlonly, and it does so against8062810dand35133f29without this commit, so not this change's. It changesscripts/render-cli-reference.py,docs/cli.mdandtests/test_repo_hygiene.py; none of those hunks overlaps mine, andmerge-treereports no conflict in them.family.pywork: the heads I could fetch are family.py:add's refusal of a member's own gitlink held only under another spelling of its row's path moves it back to that path in the index alone, so the printed remedy no longer takes the member's gitlink away from its row (#646) #670 (pr670) and scaffold-project.py and family.py:scaffold-bad-idandfamily-bad-idname an id derived from--projector--familyas derived and say to change that flag, so--project North_Windno longer says--id 'north_wind' must matchand sends the person to an--idthe naming policy then refuses (#659) #671;fix/664-surviving-mutantsandfix/696-setup-family-folderare on origin too. Clean with each. Lane 4's otherfamily.pybranches (family.pyadd/bump/remove: amembersfile deleted unstaged, agit bisect, an empty sparse-checkout file, an emptyrebase-merge/and a merge conflicted infamily.yamlmeet a refusal or remedy that does not fit (#626's gaps) #684, family.pybump: a mount with a narrowed fetch refspec is refusedmember-to-local-onlyon a false detail, a commit only a tag holds is told "a pin the rest of the world cannot fetch", a mount with nooriginis told to fix the network #686, family.py init:--created-byandGIT_AUTHOR_NAMEare never asked whether the manifest can hold them, a whitespace-onlyGIT_AUTHOR_NAMEfails after the remote exists, and a--familya Windows disk cannot hold exactly is accepted #688, family.py: the NEXT push line is a forced refspec for a branch beginning with+and fails for a branch named like a tag, and a tracking branch of 251-255 characters passes--dry-run, dies at the first commit once every remote exists #689,--local-remote-dir:family.py addtakes a directory whose name is not UTF-8 into.gitmodules, the clone-URL refusal's remedy misses a link's target and a lone surrogate, and the READMEs' clone line is rendered unquoted #693, update-shape.pyapply: a rollback line fails over a read-onlycontracts/unsaid,committed on <branch>:lists a path the commit lacks,--branch v1.0.0cannot push beside a tagv1.0.0, and a quoted"files":header is refused #674) are not on origin and not in the shared.git, so I could not check them. Predicted merges with family.py:add's refusal of a member's own gitlink held only under another spelling of its row's path moves it back to that path in the index alone, so the printed remedy no longer takes the member's gitlink away from its row (#646) #670, scaffold-project.py and family.py:scaffold-bad-idandfamily-bad-idname an id derived from--projector--familyas derived and say to change that flag, so--project North_Windno longer says--id 'north_wind' must matchand sends the person to an--idthe naming policy then refuses (#659) #671 and lane 5'sfix/646pass the two guards' files (65 passed each).Quill/Quillthatfamily.py init --name QuillWorksmakes is detected with no flag, and theQuillWorks/QuillWorksthatmake siblingsrefuses is not (#696) #703 andfix/696(setup-project.py), adopt-project.py:planrefuses--id ''and--elected-by ''by name, a derived id's refusal says to change--project, or in place to rename the source first, and an org read from a url is named as not given (#658) #699 (adopt-project.py), shape-doctor.py:submodule_configandgitmodules_entryread a holder's.gitmodulesthrough onegit config -z --list, so a section name holding a space or a byte that is not UTF-8, or a url holding NEL or U+2028, gives the doctor the mount url git andmake siblingsuse, under any locale (#668) #700, shape-doctor.py: a checkout whose owncontracts/repository-naming.yamldoes not load is CANNOT ANSWER, exit 3, for a project, where it readINVALID (naming), exit 1; any other naming exit 2 "refuses"; and the EXIT CODES list names an unfinished merge (#678) #701, validate-pins.py and shape-doctor.py: one leg's gitlink off its pin beside another leg's digest or workflow reference names the step that comes first, that gitlink put back by hand, and then the other leg'sbump-leg.pyline, so neither finding says "First clear that finding" over a finding no line can clear first (#677) #702 and shape-doctor.py: themembersrow says, member by member, whatmake siblingsdoes with the directory beside the holder, in its own word and inplace's order, so it no longer says it places a member it leaves exactly as it is, and it spells every value it quotes, so an ESC or a CR in a url no longer reaches the terminal (#669, #673) #705 (shape-doctor.py),fix/664: clean, and the inventory file passes on each predicted merge (9 passed each). bootstrap.py and validate-family.py: a member no credential would check out fails the family bootstrap, every row's gitlink is asked before any checkout, and--no-membersprints the case-variant move (#648) #682, tests/test_doctor_next_is_a_command.py: the comment-tail guard reads each string expression whole, across+and an f-string's pieces, and flags a#led by two or more blanks or a tab with or without a blank after it and a#that opens text after a part that is not a constant, socmd #x,cmd + " " + "# x"andf"{cmd}{pad}# x"no longer reach a NEXT unseen (#679) #698 and tests: validate-pins.py's workflow scan skips a directory, a dangling symlink and a looping symlink named.yml, and both of siblings.py's headers print the holder's root and folder spelled, so theS_ISREGchild check madelambda mode: True, or either path printed raw, now fails a test (#664) #706: clean.Left out, and why
docs/release-notes/v1.0.0.md:532says the.pycis left bytest_cli_reference.py's children. That is true of v1.0.0 and I left it. The v1.1.0 notes (README, AGENTS.md, docs/cli.md and the handbook:pipx install openreposhapeis the second way in and the first on native Windows, git-filter-repo is installed in install.sh's order and never with a barepip install, and a first draft of the v1.1.0 release notes (#563) #630) may want one line.--othersin the file listing. A new, untracked tool is scanned before it is added, asrglobdid; a tracked file deleted from the work tree is skipped.Checklist candidates
map(shell_quoted, words)orfunctools.partial(shell_line)is not seen by the inventory (said inhost_calls' docstring). Nothing does it today._switch_command,_print_next,_checked_mount_path._print_next_stepsprints acd {shell_quoted(work)}line the sentence's list (NEXT,switch -c,add,git rm -r --cached) does not name. It belongs to the Git Bash dialect issue.LAUNCHERSstill has no row forsrc/openreposhape/cli.py(run_child([sys.executable, ...], env)) orsetup-project.py(its ownrunwith aPYTHONvariable). Both spell the child differently fromsubprocess.run([sys.executable, ...]).Closes #665
🤖 Generated with Claude Code
Summary by Sourcery
Harden repository guards and tests so generated trees remain clean and documented launcher and quoting inventories stay accurate.
Bug Fixes:
Enhancements:
Tests: