Repository navigation
tests: a tests-git-bash job runs the launcher's and install.sh's suites in Git for Windows' own bash, so README and the handbook say Git Bash runs the one-line install, tested in CI but for a typed yes, which it cannot take there (#585) - #596
Conversation
…pe.ref` beside the command it places, and the installed command then fetches setup.sh at that ref, hands setup.sh `--shape-ref` for it and checks the doctor's clone out at it, so a command installed from a release stays on that release while `$OPENREPOSHAPE_REF` still overrides it for one run (#564) An installed command read `$OPENREPOSHAPE_REF` at every run and fell back to main, so from v1.0.0 on, with main carrying v1.1.0 work, a command installed from a release drifted off it within hours. The ref is now NAMED by the variable or by the record, and both places that honoured only the exported variable (the doctor's checkout and setup.sh's `--shape-ref`) honour either. The record is a sidecar file, never an edit to the command: the placed command stays byte for byte the file it was installed from, so the installer's own `(unchanged)` verdict and any copy vendored and checked by sha256 still compare bytes and never see the record. An install with no ref records nothing and removes any record already there, which is the command exactly as before. The ref is checked before anything is placed, and the record is checked when it is read, naming the file and the two ways out. `--version` prints the ref in force. docs/cli.md is regenerated for the `--help` paragraph that says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oShape/releases/latest/download/install.sh | bash`, installs the newest release's openRepoShape pinned to that release, installs git-filter-repo with pipx where pipx is and it is not or prints the one command that would, and ends with `openRepoShape --preflight`; README, the handbook and the v1.0.1 release notes lead with it (#564) Brett Heap, 2026-10-14: "we need it to be more simple to install ... now and after, we need a simple install process", and "v1.0.1 soon". install.sh is attached to every release, so `releases/latest/download/` is always the newest. It reads the newest tag from github.com's `releases/latest` redirect with no gh and no login, asking `gh api` only when the redirect names none; takes a named release as its one argument; fetches that release's `openRepoShape`, checks it is the command, and runs its `--install` with `$OPENREPOSHAPE_REF` set to the release, then asks the installed command's `--version` that it stayed there. Every step says what it did in one line. It never runs sudo. Exit 0 installed and ready, 1 installed but the preflight or the pin needs attention, 2 nothing installed; git-filter-repo never moves it. Everything runs from a `main` called on the last line, so a cut-off download runs nothing. README.md: step 3 and the worked example lead with the one line, the gh api and raw-URL lines stay as the by-hand way at main, and Layout gains install.sh; the cap moves 1422 -> 1450, with the reason in test_readme_is_short_enough_to_be_read. docs/handbook.html says the same, names the new README blob and adds the (#564) clause. install.sh joins SHIPPED_BASH and the macOS job's `/bin/bash -n` step, and one test holds the one line byte for byte in every file that carries it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rds a ref only with `OPENREPOSHAPE_RECORD_REF=1`, which install.sh sets, so a script that exports `$OPENREPOSHAPE_REF` around it gets v1.0.0's install; every curl that runs is HTTPS only, redirects included, at TLS 1.2 or newer; a link to the command reads its record; `--help` answers past a broken record; and install.sh says which failure it met (#564) The review on #577 (RV577, FIX-ROUND) and SonarCloud shell:S6506. openRepoShape: - The ref is recorded only when asked, or when it came from the command's own record (a re-install of itself stays pinned). workBenches exports `OPENREPOSHAPE_REF=pinned-by-workBenches-no-fetch` around `--install`; that no longer becomes a record every later run would fail on. Any other install records nothing and removes a record already there. - The raw-URL fallback curl carries `--proto '=https' --proto-redir '=https' --tlsv1.2`, written on the line. - The record is found beside the FILE, symlinks followed with a portable `readlink` loop (no `-f`, at most 40 links). - A broken record is kept as a fault and refused after the arguments are read, so `--help` still answers; `--version` and every run refuse. A record that is a directory is refused in the usual form, by a run and by an install, before anything is placed. install.sh: - Both curls HTTPS only, redirects included, TLS 1.2 or newer. - Sets `OPENREPOSHAPE_RECORD_REF=1` for the install. - A fetch the server refused (curl exit 22) asks whether the release exists; no answer at all points at the network instead. A preflight that exits 1 names what is missing; any other status says it did not run to the end. - With no pipx, `apt-get` machines are told `sudo apt-get install -y pipx && pipx install git-filter-repo==2.47.0` (printed, never run), because Debian 12 and Ubuntu 23.04+ refuse `pip install --user`. - A command that did not record the tag gets its preflight run with the tag named, not main's setup.sh; the shell-profile notes say so. - The last line is `{ main ${1+"$@"}; }`: a download cut inside it runs nothing. install.sh itself never runs sudo; the wording now says the preflight's offers, some of them sudo, each wait for a typed yes. Tests: HTTPS_ONLY in the call asserts and a guard on every curl that runs in both scripts; the opt-in, links, a directory record and `--help` past a bad record; an upper-case and a relative `location`; a non-default OPENREPOSHAPE_BIN_DIR; the apt-get branch; the network failure; the preflight's two failures; three truncated downloads; S9073 composite asserts split. docs/cli.md regenerated for the `--help` change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, the fix round on #577: install.sh itself never runs sudo while the preflight it ends with offers what is missing behind a typed yes, the command is pinned to the release tag only when asked, step 3 folds the pin into one paragraph six lines shorter, AGENTS.md says the one-line install is the human's to run, and the notes gain the steps that attach install.sh to the release (#564) - README.md: step 3's paragraph now carries what "pinned" means and the corrected sudo sentence, and leaves the exit statuses to `install.sh --help` and the release notes; the worked example's lead-in is one line. The cap moves to 1444 (from 1450 in d0cdbf4), with the reason updated in test_readme_is_short_enough_to_be_read. - docs/handbook.html: the same paragraph, the same rule in "For AI assistants", the #564 footer clause updated, and the new README blob. - AGENTS.md: "The one-line install (`install.sh`) is theirs to run: relay it, never run it." under "Never answer an install prompt", paid for by tightening the `--allow-upstream-org` paragraph's middle clause; still 490 lines, every rule kept. - docs/release-notes/v1.0.1.md: the corrected sudo sentence, the opt-in (`OPENREPOSHAPE_RECORD_REF=1`) and what a script that exports `$OPENREPOSHAPE_REF` now gets, HTTPS only, Git Bash supported but its tests skip on the Windows job (a known limit, followed up in v1.1.0), and a "Before tagging (remove at tag time)" section: upload install.sh to v1.0.1, check the `releases/latest/download/install.sh` redirect, never attach it to v1.0.0, and publish a later patch on an older line with `--latest=false`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…follow in order (land, prove the squash tree or dispatch the three-OS run on it, tag it annotated, create the release with install.sh attached and --verify-tag, check the asset by redirect and by bytes), Git Bash is said to be meant to work but untested until #585, and only install.sh's own downloads are said to be HTTPS only (#564) The second fix round on #577 (RV577). The old checklist published before attaching install.sh, could tag main through `gh release create` with no tag, uploaded an unchecked working-tree file and never tested the release commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-564-carry-to-main Conflicts, both resolved to main's side, because main retired the mechanism #577's side used (#519, #551): tests/test_repo_hygiene.py: README.md's cap stays README_CAP_BASE plus tests/readme-cap.d/; #577's inline 1422 -> 1444 raise is not taken here and becomes a ledger file of its own in the next commit. docs/handbook.html: the footer keeps main's frozen history and its closing sentence; #577's "added (#564)" clause and whole-README blob are the retired convention, and the sections' markers carry it instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the one-line install in a ledger file of its own, as main raises it, rather than #577's inline 1422 -> 1444 on release/v1.0 (#564) README.md on main is 1427 lines at its cap (1422 + 5, #550); #564's lines take it to 1449. The reason is #577's, carried with its accounting: ten lines for the two install.sh blocks, nine for step 3's paragraph, two for the by-hand ways kept at main, one for the Layout row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…S is a typed yes and the one-line install asks nothing, placing the command and git-filter-repo through pipx without sudo, and the eight sections that read README.md § Starting and § Layout record #564's README after a re-read (#564) What changed in README.md since the markers were set is #564 alone: `git diff f6eaab7 HEAD -- README.md` equals `git diff origin/main HEAD -- README.md` (the § Starting markers were set by #579, f6eaab7), and § Layout moved only by the install.sh row since #551 (5e59e3b) set its marker. Re-read against the merged README, each section in full: - masthead: its fastest path is #577's install.sh line with an accurate summary (newest release, pinned, git-filter-repo where pipx can, ends in the preflight). No change. - start: carries #577's install step, the by-hand ways at main and "Each way installs". One statement was made false: "Every action that changes the machine or the account is a typed yes" stood a few lines below install.sh installing git-filter-repo with pipx unasked. It now scopes the rule to what the tool offers and says what the one-line install does, every fact quoted from README step 3. The machine: block paragraph, the preflight step, Run it and What setup.sh actually runs stay true as written. - partway (the WARNING), ci (private legs), legs (the --explain line), doublepin (the hand-edited pin), park (openRepoTools' line, which --install still prints when install.sh calls it): nothing #564 touched. No change. - layout: carries #577's install.sh row, in README's order; "standard library only" stays true (install.sh is bash, git-filter-repo a tool). No change. Markers: § Starting f0693d8a -> cc78d254 in masthead, start, partway, ci, legs, doublepin, park; § Layout c8be0af4 -> 94701efc in layout; both values as the hygiene check printed them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #580 (#546), dd3f23a. One conflict: tests/test_repo_hygiene.py: #564's one-line-install test and #580's DOCTOR_PASSAGES test were added at the same place; both are kept, #564's first. The launcher, its test, README.md, AGENTS.md, docs/handbook.html and docs/cli.md merged cleanly, each holding both sides' hunks unchanged. README.md stays 1449 lines (cap 1449) and AGENTS.md 490 (cap 490). Every handbook marker of § Starting, § Layout and § Keeping equals its segment's fingerprint in the merged README after a re-read, so none moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…yped yes, beside step 3's one-line install, which installs git-filter-repo with pipx where it can (#564) "Nothing is installed without that typed `yes`" became "The preflight installs nothing without that typed `yes`", and the rest of the sentence is kept. The longer clause re-wraps the NOTE's last eight lines into eight, so README.md stays at 1449, its ledger cap, and every line stays within 80 columns. `py setup-project.py --preflight` stays on one line, because test_the_windows_commands_name_files_that_exist reads `py <file>` across whitespace. Re-read against the new wording, the seven sections that read § Starting: start's consent rules ("Every action the tool offers ... is a typed yes", "The one-line install asks nothing ...", "no offer at all") and its four "The tool offers the rest" panels already put the typed yes on the preflight's offers; its `machine:` block line is about --install, and partway's "Nothing is created before the typed yes" quotes the WARNING, not the NOTE; masthead, ci, legs, doublepin and park render nothing from the NOTE. No page text changes. The markers move as the hygiene check printed: cc78d254 -> c9aebc08 in masthead, start, partway, ci, legs, doublepin, park. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ore its own two acts, and the preflight it ends with asks before each offer (#564) "The one-line install asks nothing" was not true as written: install.sh ends with `openRepoShape --preflight`, which asks `Type yes to continue:` before each offer, as step 3 above it and README.md:239-241 say. Only install.sh's own two acts, placing the command and installing git-filter-repo with pipx, go unasked. Found in #587's review. Handbook text only, in the same five lines: README.md is unchanged, so no marker moves, and the hygiene check asks for none. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in the Windows job under Git for Windows' own bash, found beside its git and never from PATH, with paths spelled as Git Bash spells them, so the one-line install and the command it places are tested on Windows, and README and the handbook say Git Bash is supported (#585) The two suites wore WINDOWS_SKIP, so nothing had run `install.sh` or `openRepoShape` under Git Bash, where v1.0.1's one-line install is meant to work. They now run there; the Windows job's PowerShell step runs them with the rest of the suite. tests/conftest.py: - `BASH` is Git for Windows' `usr\bin\bash.exe`, found from `git --exec-path`. A bare `bash` there is System32's WSL launcher, which `subprocess` reaches before PATH ("Windows Subsystem for Linux has no installed distributions"). `bin\bash.exe`, which `shutil.which` finds, puts Git's `mingw64\bin` first on PATH, so Git's real `curl` shadowed the suite's fake one and the old install.sh suite reached github.com. - `BASH_SKIP` replaces `WINDOWS_SKIP` in these two files only. - `bash_path` and `native_path` convert with Git's own `cygpath`. - The `git_bash_session` fixture puts `mingw64\bin`, `usr\bin` and `MSYSTEM` where Git Bash puts them, behind any fake a test puts first. The runner's PATH already holds both; a stock Git for Windows install puts only `Git\cmd` on PATH, and there `mktemp` is missing and `bash` is the WSL launcher. The two test files: - every path handed to bash or expected from it is spelled `/d/a/...`; - every script a test writes for bash is written as LF bytes, and the fake's redirect headers and `gh` tag are written as bytes, because Python on Windows prints `\r\n` and `$(...)` keeps the `\r`; - `bash -s` runs keep `bash` as argv[0] and name `BASH` as the executable; - `--install` is checked as a command by mode 755, or under Git Bash, which keeps no mode bits, by bash's own `test -x`; - a test's own PATH holds `#!/bin/sh` wrappers on Windows, because a symlink to a Git Bash program cannot load its DLL; - an argument holding a newline reaches Git Bash through the environment, because the Windows command line splits it in two; - a new test makes a link with Git Bash's own `ln -s`: by default a copy, which follows main, and with `MSYS=winsymlinks:nativestrict` a link, which reads the record. .github/workflows/tests.yml: a step before the Windows tests fails the job when conftest finds no Git Bash, so the two suites cannot quietly skip; the macOS job's comment no longer says the Windows job skips the launcher. README.md and docs/handbook.html: PowerShell has no twin of the command, and in Git Bash the one-line install and the command are supported and tested; WSL2 runs every bash entry point, no longer "is still how you run" them. README keeps its line count. The seven handbook markers for README.md § Starting a project in a new organisation record its new hash. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-5, 2026-10-15T15:05Z) @codex review |
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. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
Reviewer's GuideThe PR enables and hardens the launcher and install.sh suites under Git for Windows Bash on Windows CI, adds platform-specific path, encoding, executable, PATH, argument, and symlink handling, and updates README and handbook documentation to explicitly support Git Bash. Sequence diagram for Windows CI running tests under Git BashsequenceDiagram
participant WindowsJob
participant GitForWindows
participant GitBash
participant TestSuites
participant Conftest
WindowsJob->>GitForWindows: git --exec-path
GitForWindows-->>WindowsJob: Git installation path
WindowsJob->>GitBash: invoke usr/bin/bash.exe
GitBash->>Conftest: load BASH and git_bash_session
Conftest->>Conftest: bash_path
Conftest->>Conftest: native_path
GitBash->>TestSuites: pytest
TestSuites-->>WindowsJob: launcher and install.sh tests pass
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
…and the openRepoShape command, tested in CI but for a typed `yes`, which the preflight the one-line install ends with cannot take there, so README and the handbook no longer call that flow supported as a whole; the tests anchor two Windows-only paths and drop an inherited `MSYS` (#585) The review on #596 (rv585, S1): under `curl ... | bash`, install.sh runs `exec </dev/null` (install.sh:277), and setup-project.py's `open_terminal()` has no Windows path, so in Git Bash the preflight that the one-line install ends with cannot take a typed `yes`. Nothing installs without one, so consent holds; the claim did not. README.md (line count unchanged, 1449): - the Windows paragraph says Git Bash runs the command and the one line, tested in CI but for a typed `yes`, which its preflight cannot take; - step 3 says the preflight's offers wait for a typed `yes` at your terminal, and that in Git Bash it can take none. docs/handbook.html: - the Windows panel says the same, at length; - the consent rules say that in Git Bash the preflight can take no `yes`, so it installs nothing; - step 3's install paragraph is `data-platform="linux mac wsl"` and is left; - the seven markers for README.md § Starting a project in a new organisation record its new hash. tests/test_openreposhape_command.py: - the fetched-setup.sh test asserts that the parent of the directory the stub ran in exists, so its "the directory is gone" line cannot pass on a path Python cannot open (`/tmp/...` read as a Windows path); - the `ln -s` test drops a `$MSYS` inherited from the caller, so its default case is Git Bash's default, a copy. tests/conftest.py: `git_bash_session` puts `clangarm64\bin` and `MSYSTEM=CLANGARM64` on PATH where Git for Windows has `clangarm64` (ARM64), `mingw64` otherwise. .github/workflows/tests.yml: the guard step's comment says what probe 1 showed: a bare `bash` runs System32's WSL launcher, because CreateProcess searches System32 before PATH, and the `bash` PATH names first, `Git\bin\bash.exe`, puts Git's real `curl` ahead of the suites' fake. A correction to the first commit's message: the launcher's suite runs `bash -s` with argv[0] `bash` and `BASH` as the executable; the installer's suite still passes `BASH` as argv[0], as before, and install.sh reads neither `$0` nor `BASH_SOURCE`. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s on one native path, so its parent-directory anchor also pins the line that says the directory is gone (#585) The two asserts now share `ran_dir`, so reverting `native_path` for the existence check alone no longer passes beside a still-true anchor. This answers the re-review's nit 1. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Brings in #597 (654c476, CI on Linux only), with #565 and #555 before it. Three conflicts: .github/workflows/tests.yml: main's file whole. #587's `/bin/bash -n install.sh` and its comment lived in the macOS job #597 removed; no bash 3.2 step is added anywhere, as #597 decided, and this pull request no longer touches tests.yml. README.md: the macOS sentence keeps #587's `install.sh` in the list and takes #597's wording: "`setup.sh`, `install.sh` and the `openRepoShape` command are written to use no syntax newer than that, though CI runs on Linux only and no run of it checks them against bash 3.2", with "all three entry points" after it. Four lines, as both sides had: README.md stays 1449 lines, its cap with tests/readme-cap.d/564.txt (+22). docs/handbook.html: the macOS panel's sentence says what the merged README says. The seven markers that read README.md § Starting a project in a new organisation (masthead, start, partway, ci, legs, doublepin, park) move to a0e0f23c306cebdf627d90225e33c277f05de64b, as the hygiene check printed; the macOS sentence is the only part of that section the merge changed, and no other part of the seven speaks of CI's runners or bash 3.2. The footer is the same on both sides. test_install_sh.py's pin check still holds: the Linux job installs git-filter-repo==2.47.0, install.sh's pin. The tests' dated history that names the macOS job stays, as #597 left it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…I) into fix/585-git-bash-windows: tests.yml is the base's, without #596's guard step in the gone tests-windows job, and the handbook keeps #597's macOS panel beside #596's Git Bash text, its seven markers moved to the merged README (#585) The base, lane 3's #587 branch, merged main 654c476, which holds #597 ("ci: run the test suite on Linux only", merged by Brett Heap 2026-10-15T16:54:43Z). #597 removed the tests-windows and tests-macos jobs, made README's macOS sentence and the handbook's macOS panel say CI runs on Linux only, and moved the seven handbook markers for README.md section "Starting a project in a new organisation". Two files conflicted. .github/workflows/tests.yml: the base's file, byte for byte (blob b2de8af). Dropped from this branch: - the step "Git Bash is the bash the launcher and installer suites run", which sat in tests-windows; - the tests-macos comment's clause that the windows job runs the command only under Git Bash. Both jobs are gone, so both hunks went with them. At this commit alone no CI job runs the two suites under Git Bash, so README's "tested in CI" is not yet true here. The next commit adds the small tests-git-bash job, on Brett Heap's word at 2026-10-15T18:20:35Z: "Option 1: keep a small Git Bash job for #596". docs/handbook.html: the base's text, with #597's macOS panel, and this branch's Git Bash text (the masthead, the Windows panel, the consent rules), which merged as text. The seven conflicts were the seven markers for README.md section "Starting a project in a new organisation": this branch's 26629e7a against the base's a0e0f23c. Each now records a88ac2d0315c9c8ed6e962ab6ae34b856346ecc9, the merged section's hash, computed with tests/test_repo_hygiene.py's own readme_segments and readme_fingerprint. README.md merged cleanly as text: #597's macOS sentence and this branch's Windows paragraph and step-3 clause are in different paragraphs. It is 1449 lines, the cap (1422 + 5 for 550.txt + 22 for 564.txt). Every other file is the base's. Against the base, this merge differs only in this branch's five files: README.md, docs/handbook.html, tests/conftest.py, tests/test_install_sh.py and tests/test_openreposhape_command.py. tests/test_repo_hygiene.py and tests/test_readme_sections.py: 141 passed on the merged tree. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l `tests-git-bash` job runs the launcher's and the installer's suites on windows-latest under Git Bash, on every trigger, as the one exception to #597 on Brett Heap's word, so README's Git Bash sentence is tested in CI again and the macOS sentence no longer says CI runs on Linux only (#585) #597 (main 654c476) dropped the Windows and macOS jobs for the whole estate, and the merge before this commit took that workflow. That left README's "Git Bash runs that command and step 3's one line, tested in CI but for a typed `yes`" with nothing behind it. Brett Heap, 2026-10-15T18:20:35Z: "Option 1: keep a small Git Bash job for #596". The option he picked: a small Windows job for just these two suites, which finishes inside the Linux job's time and keeps README's claim true, as an exception to #597's decision. The workflow: - The header keeps #597's text and adds one sentence naming the exception, with his word and its time. - `tests-git-bash`: windows-latest, `timeout-minutes: 15`, no `if:`, so it runs on a push to main, on pull requests and on a manual run. A squash lands a tree no pull-request run tested (#122), and these two suites are the only run of the one-line install on Windows. - Its steps: checkout and setup-python at the ubuntu job's two commits (`fetch-depth: 0`, because the launcher's suite clones this checkout itself in test_a_ref_the_doctor_would_check_out_is_validated); pytest 8.3.4; the git identity, appended to GITHUB_ENV in PowerShell as the old Windows job did; the guard step "Git Bash is the bash the launcher and installer suites run", which fails the job when conftest finds no Git Bash; then `python -m pytest -q -rA tests/test_openreposhape_command.py tests/test_install_sh.py`, with the old job's `--basetemp` under `runner.temp` for MAX_PATH. - Its env is the old Windows job's: PYTHONUTF8=1 (the suites decode bash's output with `text=True`, and the launcher's `--help` prints em dashes), core.autocrlf=false and core.longpaths=true. Every Windows run of these two suites, #585's four probes included, had all three. - Left out: git-filter-repo, because these suites never reach a real one (the install suite fakes it and pipx; the launcher's machine report gets a fake on the PATH it builds; on Linux the two suites pass with no git-filter-repo on PATH); and actionlint, which the ubuntu job runs on this file. tests/test_install_sh.py's pin check still finds the ubuntu job's git-filter-repo pin. The docs. README's macOS sentence and the handbook's macOS panel said "CI runs on Linux only and no run of it checks them against bash 3.2". They now say "no CI run, on Linux or in Git Bash on Windows, checks them against bash 3.2", which is true with this job and keeps README at 1449 lines, its cap. README's Windows paragraph and the handbook's Windows panel already say CI tests Git Bash, and are true again. The seven handbook markers for README.md section "Starting a project in a new organisation" record its new hash, 34723d9ae3d5e64cde648576855ce706589ffc0d. actionlint 1.7.12 on tests.yml and the two template workflows: exit 0. Linux, before this commit: tests/test_repo_hygiene.py, tests/test_readme_sections.py, tests/test_cli_reference.py and tests/test_setup_by_hand_commands.py, 168 passed; the two suites, 113 passed, and 113 passed again with no git-filter-repo on PATH. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inux only, and the `tests-git-bash` comment gives its measured minute and a half instead of three minutes (#585) Review rv585c's N1: read alone, the header's first sentence said no suite runs off Linux now that `tests-git-bash` exists, so it says the FULL suite does, and the exception sentence after it is unchanged. N3: the job's comment said "about three minutes", the probes' figure, and the PR's own first run measured 86 s, 50 s of it pytest, so it says a minute and a half; comments only. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Brings in main bcfa364: #588 (709eec1, POSIX-quoted paste lines and README's Windows sentence) and #569 (bcfa364, setup-project.py's paste lines). One conflict: docs/handbook.html: the seven markers that read README.md § Starting a project in a new organisation (masthead, start, partway, ci, legs, doublepin, park) conflicted, this branch's a0e0f23c against #588's 95f06438. Both moved to the merged section's blob, d77a1e59fe4951aa2a6f96808725d0506d4ce357, as the hygiene check printed. Both sides' prose merged on its own: #588's POSIX-quoting sentence in the Windows panel beside #587's install step, NOTE and macOS sentence. Re-read: nothing in the seven sections is made false by the other side. README.md and docs/cli.md merged cleanly with both sides' hunks, and `render-cli-reference.py --check` passes. README.md is 1449 lines (cap 1449) and AGENTS.md 492 (cap 492, raised by #566 on main). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fter it) into fix/585-git-bash-windows: the four files that conflict take this branch's copy, because main's copy of each is byte for byte #587's final head 3d8fa05, which the merge before this one already holds (#585) #587 squash-landed on main as 22eacee, whose tree is #587's final head 3d8fa05 merged onto main 2b6dbb7. The previous commit merged 3d8fa05, so this branch already holds #587's every change; main merges the commits 3d8fa05 did not have: 2b6dbb7 (#584, adopt-project.py's symlink finding) and 77bb5f0 (#615, bootstrap.py and its templates, landed after 22eacee). The squash is not an ancestor of 3d8fa05, so the merge base is 62ef256 and four files conflict although both sides hold the same #587 text: README.md, docs/handbook.html, tests/test_install_sh.py (add/add) and tests/test_openreposhape_command.py. For each, `git show 3d8fa05:<file>` and `git show origin/main:<file>` are the same blob, and 77bb5f0 does not touch it: - README.md d164131 - docs/handbook.html 59ec34d - tests/test_install_sh.py 34c7de5 - tests/test_openreposhape_command.py 5e49547 So main's copy is 3d8fa05's copy, and ours is 3d8fa05's plus this branch's change: each takes ours (`git checkout --ours`). No file was merged by hand. Every other file merged as text. Against main this merge differs only in this branch's six files: .github/workflows/tests.yml, README.md, docs/handbook.html, tests/conftest.py, tests/test_install_sh.py and tests/test_openreposhape_command.py (430+, 141-), and `git merge-tree --write-tree HEAD origin/main` gives this commit's own tree. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ix/585-git-bash-windows: README's step 3 keeps #598's pipx-or-uv paragraph and this branch's "in Git Bash it can take none" inside its 1449-line cap, the handbook's consent rule keeps both, and the seven markers move to the merged README (#585) Main moved from 77bb5f0 to 396c0ff: 55faad2 (#591), fbbbc8d (#572), 7fdec0a (#624), 3fb21d9 (#598, lane 3's #595) and 396c0ff (#570). Only #598 touches this branch's files: README step 3's install.sh paragraph, the handbook's masthead and `start`, install.sh and tests/test_install_sh.py, and it adds tests/test_install_sh_filter_repo.py. Two files conflicted. README.md, one region: step 3's install.sh paragraph. #598 rewrote it for uv and the closing readinesses; this branch had added "in Git Bash it can take none" after "each behind a typed `yes` at your terminal". Side by side that is nine lines where both have eight, and README is at its cap (1422 + 5 for 550.txt + 22 for 564.txt = 1449). The cap is not raised. Two rewordings fit it, and no fact is dropped: - "`install.sh` is attached to every release and installs the newest, with no `gh` and no login." became "`install.sh`, attached to every release, installs the newest with no `gh` and no login." - #598's "`install.sh` never runs `sudo`;" became "It never runs `sudo` itself;". "It" is install.sh, the subject of the sentence before it, and "itself" is #564's own word for the contrast with the preflight. #598's "It installs `git-filter-repo` with `pipx`, or `uv`, where it can, or prints the command, runs `openRepoShape --preflight`, and says last if the machine is ready to adopt or only to scaffold" is word for word, and so is this branch's "; in Git Bash it can take none". The eight lines rewrap at 80 characters at most, as 78 README lines already are. The Git Bash clause is still true after #598. Under `curl ... | bash`, install.sh's `[ -t 0 ] || exec </dev/null` (install.sh:342, which #598 does not touch) leaves the preflight no terminal on stdin. Under Git Bash setup-project.py runs on native Windows Python, where `open_terminal()` returns None (`os.name == "nt"`), so `can_ask()` is False, and `offer()` takes no `yes`. #598 changes what install.sh does before and after the preflight, not how the preflight reads an answer. docs/handbook.html, eight regions: - The consent rules' install.sh item keeps #598's text, "installs `git-filter-repo` with `pipx` or `uv` where one is there, and itself never runs `sudo` or installs either of them", and then this branch's clause, word for word: "; in Git Bash it can take no `yes`, so it installs nothing." - The seven markers for README.md section "Starting a project in a new organisation" (this branch's 278a1947 against #598's d1666fa6) each record badbb6b273215042ba0e8ae167fd760dc25430ee, the merged section's hash from tests/test_repo_hygiene.py's own readme_segments and readme_fingerprint. All 23 markers match the merged README. The masthead merged as text: this branch's "macOS, Linux, WSL2, Git Bash" heading and PowerShell line, beside #598's "where `pipx` or `uv` can install it ... and whether the machine is ready to adopt or only to scaffold". The `start` step-3 paragraph is #598's; it is `data-platform="linux mac wsl"`, as before. tests/test_install_sh.py merged as text. #598's hunks (`no_pipx` and `no_pipx_but_apt`'s printed commands, the docstring, and the line before the printed command, which now starts "there is neither pipx nor uv") sit beside this branch's `bash_path` in `present_on_path` and in the preflight line of the same test. tests/test_install_sh_filter_repo.py comes in unchanged. It builds on `Net`, whose `fake` this branch writes as LF bytes, and it is not added to the tests-git-bash job. Against main this merge differs only in this branch's six files: .github/workflows/tests.yml, README.md, docs/handbook.html, tests/conftest.py, tests/test_install_sh.py and tests/test_openreposhape_command.py (436+, 147-). `git merge-tree --write-tree HEAD origin/main` gives this commit's own tree. tests/test_repo_hygiene.py, tests/test_readme_sections.py and tests/test_readme_windows_quoting_sentence.py: 144 passed on the merged tree. Lane: openreposhape-5 (openRepoShape-5) 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 22 hours and 57 minutes by commenting |
…ash sentences and #606's three sentences both stay in README and the handbook, so the page's seven § Starting markers read the merged section's hash (#606) Merges main bf7b19b (#583, #625 and #596 since the last merge, 396c0ff). Only #596 touches README.md and docs/handbook.html; #583 and #625 change code and tests, taken as main has them. README.md merged without a conflict. #596 rewords three places in README § Starting a project in a new organisation (the Windows and Git Bash paragraphs of its install section) and #606 adds a sentence to the part-way box in the same section, so that section's hash is neither branch's (#606: 51702a2b, main: badbb6b2) but a third one, fe0d932b. docs/handbook.html conflicted in seven hunks, all of them the one marker line of the seven page parts that read that section (masthead, start, partway, ci, legs, doublepin, park). The text around the markers merged without a conflict, so both sides' sentences are kept: #596's Git Bash words and #606's partway and adopt mirror sentences. Each of the seven markers now names fe0d932b031e4a08fc885f93d6cd978df0d5a25c, the merged README section's fingerprint, computed with the hygiene test's own readme_segments and readme_fingerprint. The § Adopting marker, ca4b6731, stays: #596 does not touch that section. README is 1456 lines against a cap of 1422 + 5 (550.txt) + 22 (564.txt) + 7 (606.txt) = 1456. #596 added no cap file (it rewords within the cap), so the cap is unchanged by the merge. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d-copy text is tested and pushed on the current main (#610) Merges f6376d7 (the squash of #614) and the main commits between this branch's base 396c0ff and it: #625 (family.py `bump --to`), #583 (adopt-project.py branch names), #596 (README, handbook and the tests-git-bash job) and #614 (adopt-project.py `git rm` bytes). No file this PR changes is among the files main changed since 40a85e1, and `git merge-tree --write-tree` was clean (tree 30b0fbf), so no hunk was resolved by hand. The pinned shape copies still move as one re-pin. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…63-pypi-package One conflict, .github/workflows/tests.yml. Main's file is kept whole: #597's Linux-only `tests` job, #593's `concurrency:` group, and #596's header exception and `tests-git-bash` job (on Brett Heap's word "Option 1: keep a small Git Bash job for #596"). This branch adds to it only its own two parts, unchanged: the header paragraph on the `package` job, after main's, and the `package` job at the end, byte for byte as at 995e138. The jobs are `tests`, `tests-git-bash` and `package`. tests/test_install_sh.py and tests/test_openreposhape_command.py merged cleanly: each one's diff from main is exactly this branch's lines (the symlink refusal cases), and its diff from this branch exactly main's. Lane 5's `bash_path` hunks for those cases follow in their own commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o symlink refusal cases spell paths as Git Bash does, so they pass in tests-git-bash (#563, #585) Lane openRepoShape-5's work, applied as lane 5 wrote it: #596 (#585) landed first, so #590, landing second, carries these hunks. In both cases the refusal is matched against `bash_path(link)`, the launcher's case hands `OPENREPOSHAPE_BIN_DIR` over as `bash_path(bin_dir)`, and a link read back has Windows' `\\?\` prefix stripped before it is compared. `bash_path` is #596's, in tests/conftest.py, and returns the path as it is off Windows. Lane 5's evidence: on Windows (probe run 38769166462, the merge of 01e8c2c with this branch) the four cases failed without the hunks and both suites gave 117 passed with them; on Linux, 117 passed. Here, on Linux: the two suites give 117 passed, with and without Path.write_text writing CRLF. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main gained #583 (#520), #596 (#585), #614 (#527), #619 (#530), #594 (#526), #617 (#525) and #618 (#418). Three files conflicted: - .github/workflows/tests.yml: main's file (with #596's tests-git-bash job) plus this branch's three additions, byte-identical to theirs: the header paragraph on the `package` job (#590), after main's Git Bash paragraph; #599's actionlint line for release.yml; and #590's `package` job, now after tests-git-bash. actionlint 1.7.12 finds nothing in it. - README.md, the native Windows block's closing paragraph: #596 says the two-liner IS the Windows way in from PowerShell, with no twin of the `openRepoShape` command, and that Git Bash runs that command and the one line, tested in CI but for a typed `yes`; this branch says the two-liner is the way in that installs nothing and pipx's `openRepoShape` runs the same flow. The merge keeps both facts in main's seven lines: the two-liner installs nothing, pipx's command runs the same flow, and Git Bash runs step 3's bash command and its one line, tested in CI but for a typed `yes`. - docs/handbook.html: the same two sentences in the masthead's second-way paragraph and the Windows panel, each combined the same way, and the seven `Starting a project in a new organisation` markers, which now read the merged README (62737be2). No other section's marker moves. README.md is 1477 lines, its cap (1449 on main, +28 for #563). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ests, 57 of 82 milestone issues closed), with #583, #596, #614, #619, #594, #617 and #618 in their areas, #596's Git Bash facts for its placeholders, release.yml's step 1 whole, and every pull request cited beside its issue (RV630 SHOULD 3, 5, 6, nits 7, 8) - SHOULD 5: step 1 matches release.yml's header: "always on a `release/v<X.Y>` branch, which no push run tests", "A `release/v<X.Y>` branch serves while it is still at that commit", and that a tag's dispatch running at the tag's commit is GitHub's documented behaviour, not yet seen here. - SHOULD 6: #598 beside #595, #592 beside #593, #460 on #557's line, and #589's line says "where that clone is still on the machine and is no partial clone" again. The counts and the open-issue list are re-derived from `git log --first-parent bfed143..origin/main` (49) and the milestone (25 open, 57 closed: #342, #392, #393, #421, #423, #426, #432, #433, #434, #443, #444, #454, #455 and #629 joined it). New lines under adopt-project.py for #583 (#520, #420), #619 (#530), #618 (#418), #614 (#527), #594 (#526) and #617 (#525); #596 (#585) under the one-line install, CI, native Windows, the docs and the known limits, where the two placeholders for it were. - SHOULD 3: the known limit about "the installed doctor's temporary clone" is gone; the README, the handbook and AGENTS.md no longer say it. - SHOULD 1: the filter-repo line says install.sh's order, apt and dnf included. - nit 7: the release process's preconditions are said as the process this release follows; the workflow checks the environments, and nothing checks the rehearsal. - nit 8: the known limits are wrapped. - "the first on native Windows, where the one line cannot run" says PowerShell cannot run it, since Git Bash does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-release-workflow Brings in #590 at 70955e4: its merge of main (#596's `tests-git-bash` job, #593's concurrency group, #597's Linux-only `tests` job), lane 5's `bash_path` hunks and a header-comment fix. No conflict: git merged tests.yml by itself. The result is #590's file whole (jobs `tests`, `tests-git-bash`, `package`, and its header wording) plus only #599's own change: `.github/workflows/release.yml` in the actionlint step, with its comment and step name. Every other path is #590's, plus #599's own files and its pyproject.toml comment paragraph. The release gate stays ("tests",): `tests-git-bash` is not a release gate. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…) into l3-563-docs #599 now merges #590's 70955e4, which carries main 4cf5fb0 (#596's tests-git-bash job), lane 5's `bash_path` spellings in the two symlink refusal tests, and the reworded header paragraph on the `package` job. CI's tests-git-bash job failed #630's b006f16 on those four symlink cases, because this branch still held #590's older test files. Two files conflicted, as RV630's simulation predicted: - .github/workflows/tests.yml, the `package` job's header paragraph only: #590's wording. The file is now 750366b's, which is 70955e4's plus #599's actionlint lines for release.yml. - docs/handbook.html, the seven `Starting a project in a new organisation` markers, from the criss-cross merge base: this branch's side. README.md merged cleanly to b006f16's, and the handbook is b006f16's, byte for byte. tests/test_install_sh.py, tests/test_openreposhape_command.py and tests/test_openreposhape_package.py merged cleanly to 70955e4's. The merged tree differs from 750366b only in this pull request's own ten files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h, the `switch -c` remedy says to give it another name, or go back only if it holds work not merged yet, and the docs say `family/add-<Project>` (#607) (#637) ### Fix round 1 (rv607) Review rv607 of 6212c31 said CHANGES NEEDED: 0 blockers, 2 should, 7 nits. This round is one new commit, 66b1043, on top of 6212c31. No amend, and main is not merged in. Each point: - **S1, the sentence.** It sent a person back to the old branch first. That is right only where the branch holds work not merged yet. `BRANCH_LEFT` now puts another name first and going back second (the text is under "L2-46" below). It is word for word the same in `scripts/family.py`, in the pinned `validate-family.py` and in the pinned test constant, and the comments that described the old order are rewritten. Two new tests type the printed line in the states the review probed: `main` has moved since the earlier act, and a commit was made on a detached HEAD. - **S2, a guard for every `_switch_line` remedy.** A new test reads the validator's source with `ast` and fails any remedy that names `_switch_line` whose prose does not end with `BRANCH_LEFT`. On a merge of this head with #634 it fails at `_lockstep`, which is the point: whichever of #637 and #634 lands second adds the sentence there. #634's lines are not on this branch and are not touched. - **N1.** Under "Left out". - **N2.** The commit body of 6212c31 said neither tool can print the one line that works by asking first. That holds for the validator only. `family.py` could ask; it says the validator's words so the two tools keep one rule. Decision 1 says so, and the new commit's body corrects the old one. - **N3.** The merge table is refreshed: current origin/main, #620, #622, #626, #630 to #636, #638, #639 (lane 5's #611) and #634 (S2). - **N4.** Decision 1 no longer says the typed line works "in every shell". It says what a person types is "the printed line with one word changed (the branch name, or `-c` taken out)", so it keeps the printed quoting, and that the tests type both changes as printed. - **N5.** Decision 3 now says the case of the branch name "does not matter to `add`", and adds the note about case-insensitive disks. - **N6.** The `init` NEXT STEPS test read the lines with a regex whose `--family-root \S+` breaks on a root with a space in it (`C:\Users\Jane Doe`, or any `tmp_path` with a space). It now reads the words with `host_words`, as the NEXT STEPS test beside it does. Run with a space in the base temp directory, the old test fails with `AttributeError: 'NoneType' object has no attribute 'group'` and the new one passes. - **N7.** "Three existing tests take the new text" was wrong. It is two test functions plus `GITLINK_REMEDY`, a constant four cases share. Fixed under "Tests". The one new test that passes on BASE is disclosed there too. ### Fix round 2 (rv607b) The re-verify rv607b of 66b1043 said APPROVE WITH NITS: 0 blockers, 0 should, 5 nits. This round is one new commit, a277695, on top of 66b1043. No amend, and main is not merged in. It changes comments and docstrings only, so no behaviour moves and no mutation row changes. - **Nit 1.** Two comments called another name "right in every state". It is not: where the old branch holds work not merged yet, another name leaves that work behind. The comment above `BRANCH_LEFT` in `scripts/family.py` and the one in the pinned `validate-family.py` now say a new branch starts where the holder stands now, "which works in every state". The three `BRANCH_LEFT` texts are unchanged and still equal. - **Nit 3.** The guard's docstring said it finds each `_switch_line` call "however it is passed on". A switch line moved into a variable would defeat that. It now says "wherever the call stands inside the string expression that is passed on". - **Nit 4.** The N4 line above now quotes decision 1 as written. - **Nit 5, the docstring half.** The docstring of `test_another_name_works_where_main_has_moved_since` said going back to the old branch conflicts, but the test does not assert that. It now says the test shows the other name works, and that the conflict on the old branch is shown by rv607's probe and decision 2's table. No assertion is added. - **Nit 2, left as written.** The guard's control counts stay: each case fails loudly with a clear message. Checks on a277695: the diff to 66b1043 adds ten lines and removes seven, in three files, all comments and docstrings, and the `ast` of each file is equal once docstrings are blanked. The four files `test_family_switch_branch_exists.py`, `test_validate_family_branch_step.py`, `test_validate_family_unnamed_mounts.py` and `test_family_tracking_branch.py` give `96 passed in 51.34s`, and `test_repo_hygiene.py` gives `65 passed in 1.39s`. ### L2-46, the fix `family.py add`, `bump` and `remove` refuse the holder's tracking branch with `git -C <holder> switch -c family/<act>-<member>` (#487). The holder's `validate-family.py` names the same line before each `add` and `remove` it prints (#539, landed as 54041cc). If an earlier act left that branch, the line fails with `fatal: a branch named 'family/add-atlas' already exists` (exit 128), and neither tool said what to do next. Both tools now add one sentence, in the same words (`BRANCH_LEFT` in each file): > If `git switch -c` says that branch already exists, an earlier act left it: give the branch another name, which starts it where the holder stands, or, only if that branch holds work not merged yet, the same command with `switch` in place of `switch -c` goes back to it and keeps every commit on it. - `scripts/family.py`: the sentence comes on its own line, right under the `switch -c` line, in the `family-on-tracking-branch` and `family-holder-detached` refusals (`_checked_branch`), and in the dry run's `branch` lines (`_branch_said`). - `templates/family-root/scripts/validate-family.py`: the sentence ends the remedy text of `member-gitlink-absent`, `member-duplicate-id` and `member-gitlink-unnamed`, the three remedies that print `_switch_line`. The command lines under them are unchanged. ### L2-45, rides along AGENTS.md, the holder's README, its AGENTS-shape.md and `init`'s NEXT STEPS spelled the branch `family/add-<project>`, right beside `--member <org>/<Project>`. All four now say `family/add-<Project>`, the one placeholder the validator already uses for both (`PROJECT_PLACEHOLDER`, #539). **1. What the remedy prints when the branch exists: one sentence that names the way out, not a second command line.** - *One line that works in both states* (`git switch X 2>/dev/null || git switch -c X`): rejected. On Windows, `family.py` quotes its lines for PowerShell (`shell_line` uses `os.name`; README.md:155-158 says so). Windows PowerShell 5.1 has no `||`; `&&` and `||` arrived in PowerShell 7, and `family.py`'s own `_print_next_steps` docstring says 5.1 rejects `&&`. PowerShell also has no `/dev/null`. And `2>/dev/null` would hide the real reason a `switch` fails, such as a changed file it would overwrite. - *The tool checks and prints the one line that works*: rejected for the validator. Its report is read in CI: the holder's `validate.yml` runs `python3 scripts/validate-family.py` (and `--no-members`) in a runner's checkout, which holds none of the person's branches. `family.py` could check, but then the two tools would follow two rules, and one rule in the same words was the aim. So `family.py` says the validator's words; it is not that it cannot ask. - *Two printed lines, one per state*: rejected. In the validator, each indented line is a step to run in order, and going back to the branch replaces step one; it is not another step. In `family.py`, a new `git switch` line would also be quoted for PowerShell on Windows. README.md:155-158 lists exactly which `family.py` lines are quoted that way (`NEXT`, `switch -c`, `add`, `git rm -r --cached`). That sentence would become false, and fixing it would mean editing README § "Starting a project in a new organisation". Seven handbook parts record that section's hash (`grep -c` gives 7). - *Chosen*: a sentence that names both ways out. It prints no new command, so nothing new needs quoting, and the text is the same in both tools. What a person types is the printed line with one word changed (the branch name, or `-c` taken out), so it keeps the printed quoting. The tests type both changes as printed (tests below); none of them runs a shell, and Windows and macOS are not checked (see "Left out"). **2. Another name first, going back second (S1).** The reviewer's probes, and mine, in real local holders (a holder `Orbit`, members `Atlas` and `Beacon`, local bare remotes, the `gh` stub first on PATH): | State of the old branch `family/add-atlas` | `git switch family/add-atlas` (going back) | `git switch -c family/add-atlas-2` (another name) | |---|---|---| | No commit on it, `main` has not moved (rv607) | works | works | | Holds a commit of its own, not merged yet (mine: `probe-atlas.sh`, `probe-b.sh`) | works; `add` then commits on top of it, and the earlier commit is the parent | works, but the earlier commit stays only on the old branch (`git branch --contains` names `family/add-atlas` alone) | | `main` has moved: a later member (`Beacon`) was squash-merged (mine: `probe-d.sh`; test `test_another_name_works_where_main_has_moved_since`) | `add` exits 0, but `git merge-tree --write-tree main family/add-atlas` exits 1 (`CONFLICT (add/add)` in `.gitmodules`, `CONFLICT (content)` in `family.yaml`), and an untracked `members/Beacon` is left (`warning: unable to rmdir 'members/Beacon'`) | `add` exits 0, `git merge-tree --write-tree main family/add-atlas-2` exits 0, and the validator says `2 member(s)` | | Merged, then the member removed and that merged too; the old branch is still local (rv607's probe, and mine: `probe-c.sh`) | `add` says `member-already-present`, whose remedy points at `bump` and `remove`: a dead end | `add` exits 0, once the object store `remove` leaves behind is deleted, as its NOTE says (either choice needs that) | | Detached HEAD with a commit made on it (mine: `probe-detached.sh`; test `test_another_name_keeps_a_commit_made_on_a_detached_head`) | `Warning: you are leaving 1 commit behind, not connected to any of your branches`, and `git branch --contains` names no branch | the commit stays: `git branch --contains` names the new branch | So going back is right only where the branch holds work not merged yet, and another name is right in every state but that one. The sentence says so, in that order. **3. The person's earlier work on that branch is never discarded.** `git switch <branch>` keeps the branch's commits. It refuses to switch when that would overwrite a changed file. The act then commits on top of what the branch holds; the tests check that the earlier commit is the parent of the act's commit. "Give the branch another name" leaves the old branch alone. No printed line names `switch -C` (which resets a branch), `branch -D`, `reset` or `--discard-changes`; a test checks that too. **4. The placeholder: `<Project>` everywhere.** - The validator already said `family/add-<Project>` beside `--member <org>/<Project>` (#539's fix round, pinned by `tests/test_validate_family_unnamed_mounts.py`). It is the only spelling that is one placeholder for both values. - The other option, a sentence saying the branch word is the name in lowercase, does not fit: AGENTS.md is at its cap, 492 of 492 lines (`tests/test_repo_hygiene.py:633`). This change keeps it at 492. - The case of the branch name does not matter to `add`. `_checked_branch` takes any branch but the tracking one, so `family/add-IRQS`, the doc line filled in as written, is a branch `add` commits on (test). Where the tools know the name, they still print it lowercased (`_suggested_branch`). On a case-insensitive disk (macOS, Windows) `family/add-Atlas` and `family/add-atlas` can be one loose ref there. That is untested: CI is Linux only. **5. Pinned copies.** This changes two pinned holder files: `templates/family-root/scripts/validate-family.py` and `templates/family-root/AGENTS-shape.md` (`scripts/shape_materialize.py` lists the pinned set). Each holder takes them as two `upstream-changed` rows on its next `update-shape.py` run. The holder's README is rendered once and is not pinned. No test pins those files' bytes; the tests that compare them read the template at run time. Fix round 1 changes only `validate-family.py` of the two. New file `tests/test_family_switch_branch_exists.py`, 25 tests (21 before the fix round). Four are new in the fix round: - `test_another_name_works_where_main_has_moved_since` (S1, state (d)): `family/add-irqs` is left at the `main` of its day; a later member (`IRXS`, scaffolded in the test) is added on a branch and squash-merged onto `main`, so `main` has one new commit; the refusal's `switch -c`, typed as printed, fails with `already exists` (exit 128); the same line with another name works; `add` exits 0, `git merge-tree --write-tree main <branch>` exits 0, and the validator passes. - `test_another_name_keeps_a_commit_made_on_a_detached_head` (S1): the refusal is `family-holder-detached`; after the printed line with another name, `git branch --contains <the commit>` names the new branch and no other. - `test_every_remedy_that_names_a_switch_line_ends_with_the_sentence` (S2, the guard) and `test_the_guard_sees_a_remedy_left_without_the_sentence` (its control: the source with the sentence taken out of any one remedy fails it, and so does a fourth remedy built by a helper as `_lockstep` builds its, until the sentence ends it). The other 21: - `test_the_refusal_typed_as_printed_puts_the_act_on_its_branch` [add, bump, remove] x [no-branch, branch-left]: the refusal, then its line typed as printed. With no branch, the `switch -c` makes one. With a branch an earlier act left, holding a commit of its own, the `switch -c` fails with `fatal: a branch named '<branch>' already exists` (exit 128) and changes nothing, and the same command with `switch` in place of `switch -c` goes back to that branch. The act then commits on top of the earlier commit, and NEXT pushes that branch. - `test_a_dry_run_on_the_tracking_branch_says_it_too` [add, bump, remove]. - `test_a_missing_gitlinks_remedy_typed_as_printed`, `test_a_duplicate_ids_remedy_typed_as_printed` (with the `remove` run as printed) and `test_an_unnamed_gitlinks_remedy_typed_as_printed`, each in both states. - `test_the_two_tools_say_the_same_words`. - `test_each_doc_spells_the_branch_as_its_member` [AGENTS.md, AGENTS-shape.md, holder README], `test_init_next_steps_spell_the_branch_as_its_member` (read with `host_words` now, N6), and `test_the_branch_filled_in_as_written_is_one_add_commits_on`. Two existing test functions take the new text: `test_init_next_steps_make_the_branch_before_the_add` (`<Project>`) and `test_a_missing_gitlink_remedy_makes_the_branch_and_names_the_holder` (the remedy line). `GITLINK_REMEDY` in `test_validate_family_unnamed_mounts.py`, a constant four cases share (`test_a_member_mounted_by_hand_is_two_findings` x 3 and `test_a_removed_row_is_named_by_its_gitlink_and_its_section`), ends with the sentence. The sentence is pinned once, as text, in `tests/test_validate_family_branch_step.py`. Before and after, with the four changed test files copied in (`run9.sh`, the `gh` stub first on PATH): - On BASE 396c0ff: `30 failed, 66 passed` in the four files. 24 of the 25 new-module tests fail. The one that passes, `test_the_branch_filled_in_as_written_is_one_add_commits_on`, is the evidence for decision 4: it shows behaviour this PR keeps. - On 6212c31: `23 failed, 56 passed` in the three files that pin the sentence. Both new S1 tests fail there, where they assert the new wording. The two guard tests pass there: they check where the sentence stands, not its words. - On the head 66b1043: the new module alone, `25 passed`. ### Targeted runs on the head 66b1043, BASE 396c0ff - Set A, 29 files: every `tests/test_family*.py` and `tests/test_validate_family*.py`, plus `test_bump_leg.py`, `test_bump_leg_dry_run_tracking.py`, `test_bump_leg_dry_run_truth.py`, `test_bump_leg_paste_lines.py` and `test_update_shape_branch_any_directory.py`. Result: `1016 passed, 20 skipped in 511.01s`. - From set B: `test_repo_hygiene.py`, `test_agents_md_codes.py`, `test_readme_sections.py` and `test_cli_reference.py`. Result: `210 passed in 15.73s`. The fix round changes no template other than `validate-family.py`'s text, so the rest of set B (the 27 files of the first round) is not re-run. - Every skip is a platform skip that was there before this change (a case-ignoring disk, a Windows default, powershell.exe), or a template-quoting skip in `test_family_init_values.py`. A real holder `Orbit`, made with `family.py init`, and a member `Atlas`, scaffolded with `scaffold-project.py --local-remote-dir`. - **On BASE 396c0ff** (first round): `add` refuses with `switch -c family/add-atlas`. Typed as printed, that gives `fatal: a branch named 'family/add-atlas' already exists`, exit 128, and the remedy says nothing more. The validator's `member-gitlink-absent` remedy prints the same line, with the same result. - **On the head, an unmerged act** (`probe-atlas.sh`, a branch holding a commit of its own): the refusal prints the new sentence. The same command with `switch` for `switch -c` gives `Switched to branch 'family/add-atlas'`, exit 0, at the earlier commit. `add` then exits 0, its NEXT pushes `family/add-atlas`, and its commit's parent is the earlier commit. The validator carries the same sentence. - **On the head, `main` moved** (`probe-d.sh`): the printed line fails (128). With another name: `add` exits 0, `git merge-tree --write-tree main family/add-atlas-2` exits 0, `family ok: Orbit (orbit), 2 member(s)`. Going back instead: `add` exits 0, and `git merge-tree --write-tree main family/add-atlas` exits 1 with the two conflicts in decision 2. - **On the head, a detached HEAD with a commit** (`probe-detached.sh`): the printed line fails (128). With another name, `git branch --contains` names `family/add-atlas-2`. Going back with `switch`: git warns it is leaving 1 commit behind, and no branch contains it. No row needs real GitHub. Each change below was made alone in a scratch clone of the head, then the new module was run (and, for the validator changes, the two other files that pin the sentence), and the file was put back. Every change that alters behaviour makes at least one new test fail: | Change | Result | |---|---| | `family.py` `BRANCH_LEFT` back to the first round's words | 12 failed: the six refusal cases, the three dry-run cases, both new S1 tests, `test_the_two_tools_say_the_same_words` | | `family.py` `BRANCH_LEFT` one clause dropped (`only if`) | 12 failed, the same tests | | `_checked_branch` remedy with no sentence | 8 failed: the six refusal cases and both new S1 tests | | sentence printed above the `switch -c` line, not under it | 8 failed: the same eight | | `_branch_said` (dry run) with no sentence | 3 failed: `test_a_dry_run_on_the_tracking_branch_says_it_too` [add, bump, remove] | | `_switch_command` prints `switch -C` | 11 failed, both new S1 tests among them: the printed line no longer fails `already exists` | | validator `BRANCH_LEFT` back to the first round's words | 12 failed in the three files, `test_the_two_tools_say_the_same_words` among them | | validator `member-gitlink-absent` with no sentence | 5 failed: its two typed-as-printed cases, the remedy test, the guard and its control | | validator `member-duplicate-id` with no sentence | 4 failed: its two typed-as-printed cases, the guard and its control | | validator `member-gitlink-unnamed` with no sentence | 8 failed: its two typed-as-printed cases, the four `GITLINK_REMEDY` cases, the guard and its control | | **S2:** a fourth remedy added that names `_switch_line` and does not end with the sentence (built by a helper, as #634's `_lockstep` does) | 2 failed: the guard and its control, and nothing else in the three files | | **S2:** the same fourth remedy ending with `{BRANCH_LEFT}` | 79 passed | | `init` NEXT STEPS branch back to `<project>`; its `--member` lowercased | 1 failed each: `test_init_next_steps_spell_the_branch_as_its_member` | | AGENTS.md, AGENTS-shape.md, holder README: branch back to `<project>` (and AGENTS.md's `--member`) | 1 failed each: `test_each_doc_spells_the_branch_as_its_member` for that doc | The other hunks change only docstrings and comments (`_checked_branch`, `_branch_said`, `_print_next_steps`, `_switch_line`, and the comments above the two constants). They change no behaviour, so no test can catch reverting them. | Ref | Result | |---|---| | origin/main f6376d7 (#614 landed; main has moved since BASE through #625, #583, #596) | clean, tree eb91b600. A committed merge of the head with it, e26dca8c, ran set A and #625's `test_family_bump_to_local_only.py`: `1024 passed, 20 skipped in 500.52s` | | #620 a2b4f89 | clean (it conflicted in `adopt-project.py` in the first round; it no longer does) | | #622 227bc3b (lane 2, #609, `scripts/family.py`) | clean | | #626 207d4e1 (lane 4, `scripts/family.py`) | clean | | #630 82f9403, #631 9733547, #632 86c6b9c, #633 f7f5de5, #635 2c0b157, #636 4aade9f | clean | | #638 64e6621 | clean | | #634 40a85e1 (lane 2, #610: `_lockstep` prints a fourth `switch -c`) | clean, and S2 fires on it. A committed merge, 4d1a7161: the new module, `test_validate_family_branch_step.py` and #634's `test_pinned_copy_text.py` give `2 failed, 86 passed`. The two failures are the guard, `assert [124] == []` (line 124 of the merged file is `_lockstep`'s return), and its control. With one line added to `_lockstep`, `f"moving one alone is what this refuses. {BRANCH_LEFT}"` in place of the plain string, the same merge gives `306 passed` over the nine files that read the validator or the lockstep rule (`test_validate_family*.py`, `test_family.py`, `test_family_tracking_branch.py`, `test_pinned_copy_text.py` and the new module). So the second PR to land needs that one line and nothing else. | | #639 6f938ed (lane 5, #611) | conflicts in the same one sentence, in the same two places: the end of the `member-gitlink-unnamed` remedy text in `validate-family.py` (`_check_gitlinks_named`), and the `GITLINK_REMEDY` constant in `tests/test_validate_family_unnamed_mounts.py`. #611 adds "and commit that removal, since `family.py add` refuses ..." and this PR appends `BRANCH_LEFT`. Resolved as in the first round, keeping #611's clause and appending ` {BRANCH_LEFT}` in the validator and `+ BRANCH_LEFT` in the test constant, in a committed merge (92602308): `scripts/family.py` merged cleanly, and the six test files that either PR changes or pins gave `157 passed`. Neither branch was resolved. | - No compound `||` line and no state check: see decision 1. - No README.md edit: no README sentence becomes false, because no new kind of line is printed. - The docs do not mention the exists case. AGENTS.md is at its cap, and the tools print the sentence where the failure happens. - The `switch -c` in `init`'s NEXT STEPS gets no sentence: a holder `init` just made has no branch an earlier act could have left. - **A branch checked out in another worktree (N1).** Where `family/add-atlas` is checked out in another worktree of the holder, `git switch family/add-atlas` gives `fatal: 'family/add-atlas' is already used by worktree at '<path>'` (exit 128), so the going-back half of the sentence does not work there. Git names the worktree, and another name works (`probe-n1.sh`: `switch -c family/add-atlas-2` exits 0). The sentence is not made longer for it. - I could not run Windows PowerShell or macOS here. The tests read `family.py`'s lines with `host_words`, which parses PowerShell's quoting on Windows. They make the earlier branch with `commit-tree` and `update-ref`, move `main` with the same two, write no file with `write_text` and compare nothing CRLF-sensitive. `test_another_name_works_where_main_has_moved_since` runs `git merge-tree --write-tree`, which needs git 2.38 or newer. No CI job checks Windows or macOS. - **bump-leg.py: the `git switch -c bump/<role>-<sha12>` remedy fails `already exists` when an earlier bump left that branch.** `_switch_command` (scripts/bump-leg.py:1124) prints the line for the two refusals and the dry run's closing hint. Like #607, it says nothing about a branch that is already there. - **setup-project.py: the family `git -C <holder> switch -c family/add-<project>` line has the same gap.** The NEXT lines a scaffold into a family folder ends with (setup-project.py:2358) print it with no word on an existing branch. It matters only when an earlier attempt left that branch. - L2-46 ([#459 comment](#459 (comment))) - L2-45 ([#459 comment](#459 (comment))) Review: rv607 found 0 blockers, 2 should-fix and 7 nits on 6212c31 (CHANGES NEEDED), fixed in 66b1043; the re-verify rv607b found 5 nits on 66b1043 (APPROVE WITH NITS), of which nits 1, 3 and 4 and the docstring half of nit 5 are fixed in a277695, and nit 2 is left. CI (Brett Heap's ruling 27, with his fast-refresh ruling of 2026-10-15T22:48:35Z): H0 a277695 passed tests (Linux), tests-git-bash and SonarCloud; the head 62263d2 is a clean update-branch merge of main a524542 (over e041867, a clean merge of 5b29ee1), with SonarCloud, tests-git-bash and package green on it, and the local gate passed on the head itself: the family and validator set, 1016 passed (20 skipped), and the hygiene, AGENTS.md-code, README-section and CLI-reference sets, 222 passed. Landed on Brett Heap's word of 2026-10-16T11:21:49Z, "Merge #637 when its turn comes". Closes #607 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h route claims only that the one line runs, under a Windows heading that names it, and the notes carry #620, #596's `ln -s` fact, headings that fit their entries, whose YAML reader it is and who made the environments (RV630 round 3: SHOULD 1-2, nits 1-5) The handbook: - SHOULD 1: "In Git Bash, the one-line install runs too:", where it said "works as on Linux", which #596 does not support: `ln -s` makes a copy there and `chmod` changes nothing. - nit 1: the step no longer repeats the panel's definition of Git Bash, and states the caveat once, without pointing at the panel as well. - nit 2: the Windows tab's heading names the third way, "Install with pipx, fetch the flow, or use Git Bash's one line". The release notes: - SHOULD 2: the Git Bash known limit says, as #596 and the launcher's comment do, that `ln -s` makes a copy there unless `MSYS=winsymlinks:nativestrict` is set, and a copy has no `openRepoShape.ref` beside it, so it follows main, not the installed release; link it with that setting, or put `~/.local/bin` on `PATH`. - nit 3: "update-shape.py, bump-leg.py and a project's own scripts" and "scaffold, setup, family and the shared readers"; no entry moved. - nit 4: the YAML reader is the standard's own, in `repo_shape.py`, which the tools use to read `project.yaml` and the pin files. - nit 5: the release process's acts made once say who made which: the trusted publishers are Brett Heap's to make; lane openRepoShape-3 made the environments and the tag ruleset as brettheap on his word. - #620 (#601), merged from main 879d804, under adopt-project.py; the list is current to main 879d804; the counts (67 pull requests; 76 of 104 milestone issues closed at 12:43Z) and the open list (28) are refreshed, and stay placeholders. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-letter `$OPENREPOSHAPE_REPO` is a path, a Windows-spelled `$OPENREPOSHAPE_BIN_DIR` is respelled or refused, and `tests-git-bash` runs #598's installer cases (#649) (#656) #596's Git Bash follow-ups, the five lines of #649. Two commits on main a524542: **d91b3144**, the fix, and **e85573ac**, fix round 1 from rv649's review. Together they change `openRepoShape`, `.github/workflows/tests.yml` and `tests/test_openreposhape_command.py`, and nothing else. `git diff --numstat`, d91b314: `openRepoShape` +42/-4, `tests.yml` +6/-1, the test file +182/-7. e85573a: +9/-6 (comments only), +3/-2 (a comment only), +56/-27. README, the handbook and `docs/cli.md` are unchanged: every sentence about these blocks stays true (below). ### Fix round 1 (rv649) rv649 found the launcher's code correct and asked for changes: 0 blockers, 1 should, 6 nits. e85573a answers each point. It changes no line of the launcher's code and no command of the workflow: three comments and the tests. - **SHOULD-1, the `D:/…` arm had no test.** Both BIN_DIR tests now run over `D:\tools\bin` and `D:/tools/bin`, and `fake_cygpath` takes the typed value. On Windows they use `str(...)` and `.as_posix()` of a path under `tmp_path`. With the arm dropped the suite stayed green (91 passed); now 5 cases fail. - **NIT-1, the refusal's drive-answer arm had no test.** A third refusal case: a `cygpath` that answers `D:/tools/bin` with exit 0. With that arm dropped the suite stayed green; now 2 cases fail. - **NIT-2, a `python` that is Python 2, or older than 3.9, reads as present.** Named as a cost in decision 1 and in the comment above `machine_report`. No code change: `--preflight` still refuses those machines. - **NIT-3, the refusal's reason was false in Git Bash with `cygpath` off `PATH`.** Git Bash does read `D:\…` as a drive. The comment in `install_commands` and decision 3 now give the true reason. - **NIT-4, the job's opening comment named two files.** It names the three. - **NIT-5, on Windows the refusal test's value was a real `D:` drive path.** It is now a path under `tmp_path`, and the assert names that value, so a refusal that regressed would place the command where the test looks. - **NIT-6, four claims.** Fixed in this body: "rows 1-3 are checked through the tests" (now says what runs), the clone URL for each slash (`C:\src\…` gave `https://github.com/C:\src\openRepoShape.git`), the Store stub clause (dropped, see decision 1) and the report's diffstat totals (the exact `--numstat` counts are at the top). d91b314's own commit message keeps its `....` typo and its one-URL sentence in history; the squash message is this body. - **Composition and merge table.** Refreshed against origin/main 879d804 (below). #630 no longer conflicts. - **Checklist candidate 3**, from rv649, is added below. ### The fix **1. The `machine:` row for Python** (`machine_report`). - Before: the block asked `command -v python3` and nothing else. Where Python came from python.org, Git Bash has `python` and no `python3`, so the row said `python3 MISSING` and printed the line pointing at `--preflight`, which then passed with `python`. - Now: it asks for `python3`, then `python`, in `find_python`'s order. When only `python` answers, the row says `python3 present, as python`, and nothing points at `--preflight` for it. With neither, it says `MISSING`, as before. With `python3`, it says `present`, byte for byte as before. **2. A drive-letter `$OPENREPOSHAPE_REPO`** (`shape_clone_url`). - Before: `C:/src/openRepoShape` and `C:\src\openRepoShape` were read as an owner/name, so setup.sh's clone and `--doctor`'s were handed `https://github.com/C:/src/openRepoShape.git` for the first and `https://github.com/C:\src\openRepoShape.git` for the second. - Now: a letter, a colon and then `/` or `\` is a path, and goes through as it is, as `/…` and `.…` do. No owner/name holds a colon. The package's own `clone_url` (`src/openreposhape/standard.py`, `DRIVE_RE`) already read a drive letter this way. **3. A Windows-spelled `$OPENREPOSHAPE_BIN_DIR`** (`install_commands`). - Before: `--install` named the directory as typed. With `D:\tools\bin` it printed `installed at D:\tools\bin/openRepoShape` and `export PATH="D:\tools\bin:$PATH"`, which the drive's colon splits in two. It also never found the directory on a `$PATH` that holds `/d/tools/bin`, so it printed that line every time. - Now: a drive-letter value, in either slash, is spelled again with `cygpath -u`, which Git Bash has, before anything is checked or placed. Every line then names `/d/tools/bin`, and the PATH note appears only when that directory is not on `$PATH`. - Where no `cygpath` answers (none on PATH, it fails, or it answers a drive spelling again), the value is refused before anything is placed: "--install would place this command in 'D:\tools\bin', which is spelled for Windows, and no cygpath here spells it the way bash does, so the directory could not go on $PATH: the drive's colon splits it. Nothing was placed. Set $OPENREPOSHAPE_BIN_DIR to the directory as this shell spells it, then run this again." **4. `tests-git-bash` runs `tests/test_install_sh_filter_repo.py`.** That file holds #598's uv install, the two readinesses and the pasted next steps, which ran on Linux only, while README says Git Bash runs step 3's one line "tested in CI". It is the third path on the job's pytest line, with a comment saying the installer's suite is two files. The job's other lines, the header's "the launcher's and the installer's suites, and nothing else" and #630's release-notes sentence of the same meaning stay true. **5. The stale docstring.** `test_a_ref_the_doctor_would_check_out_is_validated` said the refusal "has to arrive before any clone does". The command clones first, then checks the ref, and refuses before `git checkout` is handed it. The docstring now says that. The order is unchanged. - **Line 1: `command -v` for both names, and no version check.** - The block promises to execute nothing but `gh auth status`, and `test_the_machine_report_runs_no_installer` holds it to that: every fake on its PATH logs a run. Running `find_python`'s check would break that promise. - What answers is therefore never asked its version, and that has a cost. The Microsoft Store's `python3` stub reads as present, as macOS's Command Line Tools stubs for `python3` and `git` already do (setup.sh's comment at :41-47). That is not new: the base asked `command -v python3` too, and the Store installs its `python.exe` and `python3.exe` aliases together, so `python3` answers first. - What is new is a `python` that is Python 2, or older than 3.9, where there is no `python3`. On the base such a machine said `python3 MISSING` and printed the `--preflight` pointer. Now it says `present, as python` and prints no pointer. It is rare (CentOS 7, macOS 10.14 or older, Python 2.7 from python.org), and `--preflight` still refuses it with "no Python 3.9 or newer on this machine (tried python3, python)". The comment above `machine_report` says so. - The row keeps its name `python3`, and the state carries the name that answered, in the form `gh`'s row already uses (`present, logged in`). So README's sentence ("`git`, `gh` and whether it is logged in, `python3`, and `git-filter-repo` … each `present` or `MISSING`") and the handbook's stay true, and README is not edited. Labelling the row `python` would have made both false in Git Bash. - **Line 3: respell, and refuse only where respelling is not possible.** Respelling keeps a Git Bash user's value working: `cp`, `mkdir` and the record took the Windows spelling already, and only the printed lines were wrong. Where no `cygpath` answers, nothing in the command can spell the value as bash's `$PATH` needs it: in Git Bash with `cygpath` off `PATH` the drive is real but its colon still splits `$PATH`, and on Linux `D:\tools\bin` is a relative directory with a colon in its name. No `$PATH` line could work there, so it refuses. A `cygpath` that answers a drive spelling again is refused alike. - **The tests' `install_with_machine` now names its directory with `bash_path`.** Its `$PATH` holds only the programs `--install` runs, and no `cygpath`. On Windows the old `str(bin_dir)` is a drive path, which would now be refused. Off Windows, `bash_path` changes nothing. Other tests that pass `str(...)` keep the full PATH, so on Windows they now go through Git Bash's own `cygpath`. - **`run_cmd` takes a `cwd`.** On the base, and under a mutation, the new BIN_DIR tests' `D:\tools\bin` is a relative directory. With `cwd=tmp_path` it lands in the test's own directory, never in the checkout. - **Both BIN_DIR tests run over both slashes** (`BIN_DIR_SPELLINGS`, `typed_bin_dir`). Off Windows the typed value is the fixed `D:\tools\bin` or `D:/tools/bin`. On Windows it is a path under `tmp_path`, so neither test can place a command on the machine's own `D:` drive. New, in `tests/test_openreposhape_command.py`, 27 cases: - `test_the_machine_report_finds_a_python_by_either_name`: `python` alone, both, neither (3). - `test_a_windows_spelled_bin_dir_is_spelled_the_way_bash_spells_it`: not on `$PATH`, on `$PATH`, each in both slashes (4). On Windows it is a real drive path (`str(bin_dir)` or its `as_posix()`) and Git Bash's own `cygpath`; elsewhere a fake `cygpath` answers as that one would. - `test_a_windows_spelled_bin_dir_is_refused_where_no_cygpath_spells_it`: no `cygpath`, a `cygpath` that fails, a `cygpath` that answers `D:/tools/bin`, each in both slashes (6), on the machine report's own `$PATH`. - `test_a_clone_is_handed_a_path_or_url_as_it_is`: owner/name, URL, scp, POSIX path, `C:/…`, `C:\…` and `d:\…`, each through setup.sh (a setup.sh that prints `$OPENREPOSHAPE_REPO`) and through `--doctor` (a `git` that prints the URL it is handed and fails) (14). Nothing is cloned or fetched. **On the base a524542** (this head's test file over the base's command): **17 failed, 80 passed** in 62.32s. The failures are exactly the new cases that need the fix: `python-alone`, the four respelling cases, the six refusal cases and the six drive-letter cases. The other 10 new cases pass there too, as they should. **On the head e85573a** (Linux, `dirty=0`): - `tests/test_openreposhape_command.py`, `tests/test_install_sh.py`, `tests/test_install_sh_filter_repo.py`, `tests/test_openreposhape_package.py`: **220 passed** in 175.18s. The package suite imports 13 of the launcher's cases and swaps `run_cmd`, so it is in the set. - `tests/test_repo_hygiene.py`, `tests/test_workflow_concurrency.py`, `tests/test_release_workflow.py`: **138 passed** in 14.46s. - The head merged with main 879d804 (a probe commit, b6cfafe3): the launcher's, the two installer files, the package suite and `tests/test_repo_hygiene.py`, **297 passed** in 188.95s. - **The three files `tests-git-bash` runs, with Windows' `write_text` emulated**: a plugin made every `Path.write_text` in the test process write CRLF, as it does on `windows-latest`. **162 passed** in 121.06s (97 + 47 + 18). A probe test shows the plugin takes effect (it passes with the plugin and fails without it). - **actionlint 1.7.12**, with shellcheck on PATH, on `tests.yml` alone and on the four files the CI job names: exit 0. d91b314's own `tests-git-bash` run was green on `windows-latest`. It ran the first round's 21 new cases and `tests/test_install_sh_filter_repo.py` there, so Git Bash's own `cygpath` answered the backslash respelling on a real drive path, and the five existing tests that pass `str(...)` and now go through it passed. What was checked, and what was not: - **Checked, from #596's Windows runs (113 passed on 5f001de, and its probes):** a fake that Python writes with `#!/bin/sh` runs under Git Bash with no mode bits, and `command -v` finds it. A script piped in text mode reaches Git Bash's bash as CRLF, and bash drops the CR. A `$OPENREPOSHAPE_BIN_DIR` set from Python reaches bash unconverted: #596's probe 4 printed `D:\a\_temp\…\bin/openRepoShape`, which is why the respelling now runs in those tests. - **Checked on Linux:** LF bytes (the CRLF emulation above). No assert in the test file compares a path bash printed with a Python path. - **Not checked, until e85573a's own run:** on Windows, the slash spelling (`as_posix()`) through Git Bash's real `cygpath`, the refusal's values under `tmp_path` on the machine report's `$PATH`, and the new drive-answer case. If one fails there, its log shows the typed value and the directory. One script ran each row against the base's command and the head's, offline, with a `gh` stub first on PATH. Row 3's slash lines ran again for this round. | Row | Base a524542 | Head e85573a | |---|---|---| | 1. PATH with `git`, a logged-in `gh`, `python`, `git-filter-repo`, no `python3` | `python3 MISSING`, then `run: openRepoShape --preflight …`; the log holds only `gh auth status` | `python3 present, as python`, no `run:` line; the log holds only `gh auth status` | | 2. `OPENREPOSHAPE_REPO=C:/src/openRepoShape` | setup.sh clones `https://github.com/C:/src/openRepoShape.git`; the doctor's `git clone` too | `C:/src/openRepoShape`, both | | 2. `OPENREPOSHAPE_REPO=C:\src\openRepoShape` | `https://github.com/C:\src\openRepoShape.git`, both | `C:\src\openRepoShape`, both | | 2. `OPENREPOSHAPE_REPO=acme/openRepoShape` | `https://github.com/acme/openRepoShape.git` | the same | | 3. `OPENREPOSHAPE_BIN_DIR='D:\tools\bin'`, no `cygpath` | exit 0, `installed at D:\tools\bin/openRepoShape`, `export PATH="D:\tools\bin:$PATH"` | exit 2, the refusal above, nothing placed | | 3. `OPENREPOSHAPE_BIN_DIR='D:/tools/bin'`, no `cygpath` | exit 0, `installed at D:/tools/bin/openRepoShape`, `export PATH="D:/tools/bin:$PATH"` (on Linux, a relative directory named `D:` in the working directory) | exit 2, the same refusal with `'D:/tools/bin'`, nothing placed | | 3. the same, a fake `cygpath -u` answering `<dir>/d/tools/bin` | the same as with none: `cygpath` is never asked | exit 0, `installed at <dir>/d/tools/bin/openRepoShape`, `export PATH="<dir>/d/tools/bin:$PATH"` | | 4. `tests-git-bash`'s pytest line | two files | three files | | 5. the docstring | "the refusal has to arrive before any clone does" | "clones this checkout first, then checks the ref" | Rows 1-3 need Windows to be real: there, `python` is a real python.org install, and `cygpath` and the drive letter are Git Bash's own. On Linux they are fakes. The first Windows run runs rows 1-3's tests under Git Bash: row 3 with Git Bash's own `cygpath` on a real drive path, rows 1 and 2 with a fake `python` and a fake `git`, so a real python.org install and git's own reading of `C:/…` stay unchecked. Each row reverts one hunk of the launcher, or one part of the BIN_DIR hunk, in a throwaway clone, then runs the 27 new cases. | Hunk reverted | Result | Failing cases | |---|---|---| | none (the head) | 27 passed | — | | `shape_clone_url`'s drive-letter arm | 6 failed | the six drive-letter cases, setup.sh and doctor | | `machine_report`'s `python` arm | 1 failed | `python-alone` | | the BIN_DIR block, whole | 10 failed | the four respelling cases and the six refusal cases | | the `cygpath -u` call (always refuse) | 4 failed | the four respelling cases | | the refusal (keep the value as typed) | 6 failed | the six refusal cases | | `\|\| spelled=""` after the `cygpath` call | 2 failed | `cygpath-fails`, both slashes: `set -e` exits with `cygpath`'s status, 1, not the refusal's 2 | | **the `D:/…` arm of the drive case (SHOULD-1)** | 5 failed | the two slash respelling cases and the three slash refusal cases (was green) | | the `D:\…` arm of the drive case | 5 failed | the same five, backslash | | **the refusal's drive-answer arm, `[A-Za-z]:*` (NIT-1)** | 2 failed | `cygpath-answers-a-drive`, both slashes (was green) | | the refusal's empty-answer arm, `""` | 4 failed | `no-cygpath` and `cygpath-fails`, both slashes | | `--` dropped from `cygpath -u -- "$dir"` | 4 failed | the four respelling cases: the fake answers only `-u -- <value>` | | the respelling after the placement, not before | 4 failed | the four respelling cases | Not probed by a test: the `tests.yml` line (CI configuration, checked by actionlint and by this PR's own Windows run), the docstring, and `install_with_machine`'s `bash_path` (identity off Windows; on Windows it is what keeps four machine-report tests from the new refusal). | Against | Result | |---|---| | origin/main 879d804 (d4a7fab, then 879d804, since the last table's 95780dd) | **clean**. Of this PR's files, the three suites', README and the handbook, only `tests/conftest.py` has moved since the base a524542 (new helpers for setup-project's tests), and nothing since the last table's 95780dd. The head merged with main (a probe commit, b6cfafe3) passes the suites below. | | lane 3's #630 61b3907 | **clean**. It conflicted at ca5c49f, at the `tests-git-bash` pytest line, because its base was older than main's #590; its head now holds main d4a7fab, and it does not change `tests.yml`. It changes `AGENTS.md`, README, `adopt-project.py`, the handbook, `docs/cli.md`, the release notes and four test or render files, none of which this PR touches. | | #620 9f2b67c, #621 10dda17, #622 55666f3, #623 fc08a28, #627 83ca2db, #628 d64252a, #631 21bdac8, #634 b7472df, #636 30a52c0, #637 62263d2, #638 b393e1f, #639 08b6245, #642 1a42df4, #643 1fe3764, #644 733f853 | **clean** (#623 changes `tests/conftest.py`; #627 changes README and the handbook) | | #641 e87fcb0 | conflicts in `scripts/family.py`, and so does #641 against main alone: its own conflict, not an overlap with this PR. | | lane 5's `fix/645-fixture-copy-maintenance-race` accc7d2, `fix/646-add-member-variant-remedy` 79754b9, `fix/647-branch-left-bump-leg-setup` be1bff9 (#657), `fix/648-bootstrap-and-gate-order` 852c5c4 | **clean**, all four | - **The Store stub check.** See the decision on line 1. `--preflight` is where a stub is told apart. - **`install.sh`'s own lines.** It names a Windows-spelled `$OPENREPOSHAPE_BIN_DIR` as typed in its `preflight:` and `done:` lines, though the command it installs now prints `/d/…`. #649 names `--install`'s PATH note, and `install.sh` prints no PATH line. Checklist candidate 1. - **`--help`.** Its `$OPENREPOSHAPE_REPO` and `$OPENREPOSHAPE_BIN_DIR` lines stay true, so `docs/cli.md` is not regenerated. - **`D:`, `D:tools` and UNC values.** Checklist candidate 3. 1. **`install.sh` echoes a Windows-spelled `$OPENREPOSHAPE_BIN_DIR` as typed.** It builds `target="$bin_dir/openRepoShape"` from the raw value, so in Git Bash with `D:\tools\bin` its `preflight:` and `done:` lines say `D:\tools\bin/openRepoShape`, while the command's own lines say `/d/tools/bin/openRepoShape`. Both spellings work in Git Bash, so nothing breaks. On a machine with no `cygpath`, the command's `--install` now refuses the value, and `install.sh` stops with "the <version> openRepoShape's --install refused (exit 2); its own refusal above says why", which is right but could refuse earlier, before the fetch. 2. **The PATH note is not quoted for every directory.** `--install` prints `export PATH="$dir:$PATH"` with the directory between double quotes as it is. A directory holding `"`, `$`, a backquote or `\` prints a line that does not do what it says, and one holding `:` cannot be on `$PATH` at all. A drive letter was the one case a person in Git Bash meets by default; the rest need a deliberately odd directory name. 3. **Two kinds of value slip past both new arms** (rv649). `$OPENREPOSHAPE_BIN_DIR` set to `D:` or `D:tools` matches neither respelling arm, so it is kept as typed and prints `export PATH="D:tools:$PATH"`, as on the base. A UNC `\\server\share\…`, as `$OPENREPOSHAPE_BIN_DIR` or `$OPENREPOSHAPE_REPO`, is never respelled (the PATH note's `\\` collapses) or becomes a github.com URL. The package's `clone_url` passes a UNC path through on Windows (`os.path.isabs`). Review: rv649 found 0 blockers, 1 should-fix and 6 nits on d91b314 (CHANGES NEEDED); the fix round e85573a answers all seven, so both mutations that survived now fail a test, and lane 5's coordinator checked it. CI (Brett Heap's ruling 27, with his fast-refresh ruling of 2026-10-15T22:48:35Z): H0 e85573a passed tests (Linux), tests-git-bash (windows-latest) and SonarCloud; the head 022bd50 is a clean update-branch merge of main 8062810, with SonarCloud, tests-git-bash and package green on it, and the local gate passed on the head itself: the launcher, installer and package set, 220 passed, and the hygiene, workflow and README-section set, 214 passed. Landed on Brett Heap's word of 2026-10-16T13:07:34Z, "merge #656 and #657 when green". Closes #649 Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…KEEPS ITS NAME (which `--project` must be), ITS IDENTITY AND ITS FULL HISTORY, and the notes are current to main de92614, with seven more pull requests, lane 2's re-run-`plan` line, lanes 4's and 1's known limits, Developer Mode for a Git Bash symlink and the trusted publishers (#563, L4-47) - L4-47 (#457, lane 4 on #630 at 14:13:32Z): README.md:771-772 and docs/handbook.html:1276-1277 say "it KEEPS ITS NAME (which `--project` must be), ITS IDENTITY AND ITS FULL HISTORY, and becomes the assembly root". Two lines each, no line added: README.md is still 1473, its cap. The § Adopting marker moves to b23e9f98, text and marker; no other marker moves. - The notes, "What changed since v1.0.1": #628 (#600), with lane 2's line for a plan written before v1.1.0 (#473, 12:53:40Z) after it, and #621 (#602) under adopt-project.py; #657 (#647) under update-shape.py, bump-leg.py and a project's own scripts; #639 (#611) under scaffold, setup, family and the shared readers; #656 (#649), #698 (#679) and #627 (#606) under install, CI and the docs. Every pull request since bfed143 is cited (`git log --first-parent bfed143..de92614`, 74). - Known limits: lane openRepoShape-4's ten lines (#630 issuecomment-6217406337: L4-05, -06, -07, -08, -17, -43, -45, -54, -24, -156) and lane openRepoShape-1's twenty-six D9 lines (#457 issuecomment-6214768828), as two lists, one sentence each; and the Git Bash limit says that Windows lets a non-administrator make a symlink only in Developer Mode, as #596 does. - How this release is cut: the trusted publishers are filled from Brett Heap's 13:04:43Z ("the pypi are added") and 13:07:56Z ("they match what you listed"). - The counts (74 pull requests; 83 of 135 milestone issues closed at 17:16Z), the "current to main" sha and the open list (52) are at main de92614, still placeholders for tag time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-5 (openRepoShape-5)
Claim: #585 (comment)
What changed and why
Fix round 7: main after #598 (3fb21d9) (head 52524fe). Lane 3's #598 (#595) landed on main as 3fb21d9. It rewrote README step 3's
install.shparagraph and the handbook's masthead andstartforuv, and for the two readinesses install.sh now ends with. It also changedinstall.shandtests/test_install_sh.py, and addedtests/test_install_sh_filter_repo.py. One merge commit, 52524fe, merges main 396c0ff: 55faad2 (#591), fbbbc8d (#572), 7fdec0a (#624), 3fb21d9 (#598) and 396c0ff (#570). No other change.install.shparagraph). docs/handbook.html had 8: the consent rules'install.shitem and the seven § Starting markers.tests/test_install_sh.pymerged as text, and so did the handbook's masthead andstart.git merge-tree --write-tree 52524fe3 origin/maingives 52524fe's own tree, 6d8ffea5.git diff --stat origin/main 52524fe3names only this PR's six files (436+, 147-).The README and handbook resolution with #598 (52524fe).
yesat your terminal". install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598 rewrote the same paragraph: "It installsgit-filter-repowithpipx, oruv, where it can, or prints the command, runsopenRepoShape --preflight, and says last if the machine is ready to adopt or only to scaffold.install.shnever runssudo; the preflight offers what is missing, each behind a typedyesat your terminal."pip install --user, and two readinesses at the end,adoption:thendone:(#595) #598 has eight, and README was at its cap. The cap was not raised. Two rewordings fit it, and no fact is dropped:install.shis attached to every release and installs the newest, with noghand no login." became "install.sh, attached to every release, installs the newest with noghand no login."pip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's "install.shnever runssudo;" became "It never runssudoitself;". "It" is install.sh, the subject of the sentence before it, and "itself" is One-line install:curl -fsSL …/releases/latest/download/install.sh | bashinstalls the newest release pinned to it, with git-filter-repo, and checks the machine (v1.0.1) #564's own word for the contrast with the preflight.pip install --user, and two readinesses at the end,adoption:thendone:(#595) #598'spipx/uvsentence and this PR's clause are word for word. README is 1449 lines, its cap (1422 + 5 for550.txt+ 22 for564.txt).pip install --user, and two readinesses at the end,adoption:thendone:(#595) #598. Undercurl … | bash,[ -t 0 ] || exec </dev/null(install.sh:342; install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598 moved it down from :277 and left its text alone) leaves the preflight no terminal on stdin. In Git Bash, setup-project.py runs on native Windows Python, whereopen_terminal()returns None (os.name == "nt", setup-project.py:1372). Socan_ask()is False, andoffer()takes noyes. install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598 changes what install.sh does before and after the preflight, not how the preflight reads an answer.install.shitem keeps install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's text, "installsgit-filter-repowithpipxoruvwhere one is there, and itself never runssudoor installs either of them", and then this PR's clause, word for word: "; in Git Bash it can take noyes, so it installs nothing."start. They merged as text: this PR's "macOS, Linux, WSL2, Git Bash" heading and PowerShell line, beside install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's "wherepipxoruvcan install it … and whether the machine is ready to adopt or only to scaffold". Thestartstep-3 paragraph is install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's. It isdata-platform="linux mac wsl", as before, so it carries no Git Bash clause.pip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's d1666fa6. All seven now recordbadbb6b273215042ba0e8ae167fd760dc25430ee, computed withtests/test_repo_hygiene.py's ownreadme_segmentsandreadme_fingerprint. That driver reproduces both parents' hashes (main d1666fa6, 4c3b72a 278a1947). All 23 markers match the merged README.tests/test_install_sh.py. install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's hunks areno_pipxandno_pipx_but_apt's printed commands, the test's docstring, and the line before the printed command, which now starts "there is neither pipx nor uv". They sit beside this PR'sbash_pathinpresent_on_pathand in the same test's preflight line. Both sides' tests keep their meaning.Fix round 6: retarget to main after #587 (22eacee) (head 4c3b72a). #587 squash-landed on main as 22eacee at 2026-10-15T20:27:52Z, and GitHub retargeted this PR to
main. 22eacee's tree, 831e71db, isgit merge-tree --write-tree 2b6dbb78 3d8fa05e: main holds exactly #587's final head 3d8fa05. So this round adds two merge commits and no other change:3080ac0 merges install.sh and openRepoShape --install, carried into main from v1.0.1 (#577): the one line
curl -fsSL https://github.com/opensoft/openRepoShape/releases/latest/download/install.sh | bashinstalls the newest release's command pinned to that release, installs git-filter-repo with pipx where it can, and ends with the preflight (#564) #587's final head, 3d8fa05. It conflicted only in README.md and docs/handbook.html, with lane 4's bump-leg.py and update-shape.py quote the paste lines that named a root or a path bare, the doctor'squote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588 (709eec1), which 3d8fa05 had merged from main. The resolution keeps both sides' text (below).4c3b72a merges main, 77bb5f0: 22eacee, plus 2b6dbb7 (adopt-project.py: where git checks a symlink out as a plain file,
check's finding for a link kept above a path the shape writes says the clone holds it as a file, soscripts -> tools/deepno longer says where the scripts land and sends the person to change where it points, on toadopt-symlinks-off(#531) #584), which the squash carried but 3d8fa05 lacked, and bootstrap.py: a leg on another branch whose tracking branch is behind the pin is fast-forwarded and checked out as one at the pin already is, and the family holder's AGENTS-shape.md and bump-leg.py say bootstrap places a leg where that loses no commit (#603) #615's 77bb5f0, which landed after it. Four files conflicted, because the squash is not an ancestor of 3d8fa05 (merge base 62ef256). For each of them, main's copy is the same blob as 3d8fa05's, and 77bb5f0 does not touch it. Each therefore takes this branch's copy, which is 3d8fa05's copy plus this PR's change:3d8fa05e:=origin/main:(77bb5f0) =22eaceed:No file was merged by hand.
git merge-tree --write-tree 4c3b72ae origin/maingives 4c3b72a's own tree, 97843ec9, so the head holds all of main.git diff --stat origin/main 4c3b72aenames only this PR's six files:.github/workflows/tests.yml, README.md, docs/handbook.html,tests/conftest.pyand the two suites (430+, 141-).The README and handbook resolution with #588 (3080ac0). #588 ends README's "Windows, natively" and the handbook's Windows panel with one sentence: "Printed commands are quoted for a POSIX shell, so paste them into Git Bash. Only
shape-doctor.py's lines,setup-project.py's family lines andfamily.py'sNEXT,switch -c,addandgit rm -r --cachedlines are quoted for PowerShell, on Windows." That sentence now follows this PR's Git Bash sentences, word for word.tests/test_readme_windows_quoting_sentence.pyholds the handbook to README's words, and it passes.openRepoShapecommand in step 3 below is a macOS, Linux and WSL2 convenience with no Windows twin, because on Windows there is nothing to install. Printed commands are quoted …"openRepoShapecommand. Git Bash runs that command and step 3's one line, tested in CI but for a typedyes, which its preflight cannot take there. Printed commands are quoted …"quote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588 had six, and README was at its cap. So "no twin of theopenRepoShapecommand in step 3 below" became "no twin of step 3'sopenRepoShapecommand". Step 3 is "Install theopenRepoShapecommand", so the meaning is the same. The six lines rewrap, and README stays at 1449 lines, its cap (1422 + 5 for550.txt+ 22 for564.txt). No raise.quote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588's other README hunk, the tighterpython setup-project.pyand "Two commands rather than one pipe" paragraphs, merged as text.openRepoShapecommand to install in PowerShell … so none of its offers can be accepted there." and then bump-leg.py and update-shape.py quote the paste lines that named a root or a path bare, the doctor'squote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588's sentence, unchanged.quote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588's d77a1e59) now record278a19479524fe8aae292daaca84492902b140ed, the merged section's hash. It was computed withtests/test_repo_hygiene.py's ownreadme_segmentsandreadme_fingerprint, and all 23 handbook markers match the merged README.quote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588's sentence is true in Git Bash too: the named tools run under native Windows Python there, whereos.nameisnt, so their lines are quoted for PowerShell, "on Windows", as it says. See checklist candidate 7.Fix round 5 (head fe58d66). One comments-only commit to
tests.yml, for the re-review's N1 and N3. The header says the FULL suite runs on Linux only, so read alone it stays true beside the exception. The job's comment gives the measured "about a minute and a half (86 s on the first run, 50 s of it pytest)" where it said about three minutes.Fix round 4 (head 5f001de). Brett Heap merged #597 ("ci: run the test suite on Linux only") at 2026-10-15T16:54:43Z, which removed the Windows job these suites ran in. His word at 2026-10-15T18:20:35Z: "Option 1: keep a small Git Bash job for #596". That round added two commits:
l3-564-carry-to-mainb23b245 (install.sh and openRepoShape --install, carried into main from v1.0.1 (#577): the one linecurl -fsSL https://github.com/opensoft/openRepoShape/releases/latest/download/install.sh | bashinstalls the newest release's command pinned to that release, installs git-filter-repo with pipx where it can, and ends with the preflight (#564) #587's branch, this PR's base until fix round 6), which held main 654c476 and so ci: run the test suite on Linux only #597.tests.ymlbecame that branch's, byte for byte: the guard step and the macOS comment edit went with the two jobs ci: run the test suite on Linux only #597 removed. The handbook keeps ci: run the test suite on Linux only #597's macOS panel beside this PR's Git Bash text, and its seven markers were moved.tests-git-bashjob, and fixes the macOS sentence that ci: run the test suite on Linux only #597 made say "CI runs on Linux only".v1.0.1's one-line install (#564) is meant to work in Git Bash on Windows, but nothing ran it there:
tests/test_openreposhape_command.pyandtests/test_install_sh.pyworeWINDOWS_SKIP. They now run in thetests-git-bashjob, under Git Bash.Which bash.
BASHintests/conftest.pyis Git for Windows'usr\bin\bash.exe, found fromgit --exec-path, never from PATH. Probe 1 onwindows-latestshowed why:bashthroughsubprocessis System32's WSL launcher. It prints "Windows Subsystem for Linux has no installed distributions." in UTF-16. CreateProcess searches System32 before PATH.shutil.which("bash")findsGit\bin\bash.EXE. That launcher puts Git'smingw64\binandusr\binfirst on PATH, so Git's realcurlcame before the suite's fake one. The old install.sh suite, run that way, reached github.com and printed the real newest release:release: v1.0.1, the newest release of opensoft/openRepoShape (https://github.com/opensoft/openRepoShape/releases/latest).A
git_bash_sessionfixture puts Git'smingw64\bin(orclangarm64\binon ARM64),usr\binandMSYSTEMwhere Git Bash puts them, behind any fake a test puts first.BASH_SKIPreplacesWINDOWS_SKIPin these two files only.What the tests now do on Windows.
/d/a/...). The conversion uses Git's owncygpath(bash_path,native_path). Git Bash convertsHOME,TMPDIRandPATHitself.ghtag are written as bytes: Python on Windows prints\r\n, and$(...)keeps the\r.noacl, sochmod 755changes nothing. A file that starts with#!runs anyway.--install's output is checked by mode 755 on POSIX, and by bash's owntest -xon Windows.#!/bin/shwrappers. A symlink to a Git Bash program cannot load its DLL: with symlinks,mktempfailed and install.sh refusedcould not make a temporary directory.v1\nforgedarrived as "two versions given". In Git Bash, bash hands bash the argument whole, so the test now does the same.ln -s. A new test makes a link with Git Bash's ownln -s, after dropping any$MSYSthe caller set. By default that is a copy, which has noopenRepoShape.refbeside it and so follows main. WithMSYS=winsymlinks:nativestrictit is a real link, which reads the record.ran_dir = native_path(ran_in)(fix round 3), so the existence check cannot be reverted toPath(ran_in)alone.ghshim test already could not pass with the shim unreachable: it asserts that the shim's own log holds exactlyauth status, and only the shim writes that log. Probe 4 reverted itsos.pathsepand it still passed: the shim stayed reachable either way, most likely through MSYS's conversion of PATH, which the log does not show.The workflow (fix round 4).
tests-git-bashjob below runs the launcher's and the installer's suites, and nothing else, on Windows under Git Bash."tests-git-bash:runs-on: windows-latest,timeout-minutes: 15, and noif:, so it runs on every trigger the workflow has: a push to main, a pull request, a manual run. Its steps:fetch-depth: 0;python -m pip install --disable-pip-version-check pytest==8.3.4;GITHUB_ENVin PowerShell, as the old Windows job did;python -m pytest -q -rA tests/test_openreposhape_command.py tests/test_install_sh.py --basetemp=${{ runner.temp }}\t.PYTHONUTF8=1,core.autocrlf=falseandcore.longpaths=truethroughGIT_CONFIG_COUNT.usr\bin\bash.exe(pointing to conftest), and why it runs on a push to main.The docs.
yes, which the preflight the one-line install ends with cannot take there. bump-leg.py and update-shape.py quote the paste lines that named a root or a path bare, the doctor'squote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588's sentence about POSIX-quoted printed commands follows (fix round 6).pip install --user, and two readinesses at the end,adoption:thendone:(#595) #598) runsexec </dev/nullundercurl … | bash, and setup-project.py'sopen_terminal()has no Windows path.yes, so consent holds.tests-git-bashmakes it true again, so its text is unchanged.yes. Since fix round 7, both clauses follow install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598'spipx-or-uvtext, and README's paragraph is reworded to fit (above).data-platform="linux mac wsl", so it is already scoped away from Windows, and is left.550.txt+ 22 for564.txt).badbb6b273215042ba0e8ae167fd760dc25430ee(fix round 7, after install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598). Fix round 4's merge set them toa88ac2d0…, 5f001de moved them to34723d9a…for the macOS sentence, and fix round 6 to278a1947…after bump-leg.py and update-shape.py quote the paste lines that named a root or a path bare, the doctor'squote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588. Every hash was computed withtests/test_repo_hygiene.py's ownreadme_segmentsandreadme_fingerprint.Decisions and their evidence
PYTHONUTF8=1: the suites decode bash's output withtext=True, which uses the locale's code page (cp1252) outside UTF-8 mode, and the launcher's--helpprints em dashes (openRepoShape:108, :119-120).core.autocrlf=falseandcore.longpaths=true: every Windows run of these suites had both, Git Bash on Windows: run the launcher and install.sh tests under Git Bash in the Windows job, so the one-line install is tested there (follow-up to #564) #585's four probes included, and no run without them exists.fetch-depth: 0:test_a_ref_the_doctor_would_check_out_is_validatedsetsOPENREPOSHAPE_REPOto this checkout, so the launcher clones it, and every Windows run of it cloned a full one.--basetemp=${{ runner.temp }}\t: the old job's short temp root, for MAX_PATH.git-filter-repoandpipxamong them. The launcher's machine report gets a fakegit-filter-repoon the PATHmachine_pathbuilds. On Linux, with every PATH directory that held agit-filter-reporemoved (filter-repo=nonein the run log), the two suites gave 113 passed.test_it_installs_the_git_filter_repo_ci_runsstill finds the ubuntu job's pin.usr\bin\bash.exe, notbin\bash.exe, and not PATH. The probe-1 lines quoted above.git_bash_sessionis needed, though the runner's own PATH hides it.bin,usr\binandmingw64\bin, withbinbefore System32 (probe-1 PATH dump). So reverting the fixture changes nothing there (probe 2: 113 passed).Git\cmdon PATH. Probe 3 ran the two files on exactly that PATH, wherewhich mktempisNoneandwhich bashisC:\Windows\system32\bash.EXE:bash: line 199: mktemp: command not foundandthis machine has no mktemp, which the install needs.bash -sruns still take their script through a text-mode pipe. That pipe hands bash CRLF on Windows, but Git Bash's bash drops a carriage return before a newline: a CRLF script piped to it printedcrlf-okand exited 0 (probe 1). Sending bytes instead changed no result (mutationtext-stdin, 113 passed), so that change was dropped.os.symlink: ok), so the three existing symlink tests run there unchanged.yesis asked in Git Bash. Typed at a Git Bash prompt,openRepoShape <Project>reads itsyesthrough native Windows Python. Whether that Python sees a console depends on Git Bash's pseudo-console support, and nobody has confirmed it on a Windows machine.Tests
dirty=0):tests/test_install_sh_filter_repo.py(install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's, unchanged): 18 passed in 7.32s;tests/test_repo_hygiene.py,tests/test_readme_sections.py,tests/test_cli_reference.py,tests/test_setup_by_hand_commands.pyandtests/test_readme_windows_quoting_sentence.py: 171 passed in 25.43s.git grep -l 'install.sh' tests/findsconftest.py,readme-cap.d/564.txt,test_install_sh.py,test_install_sh_filter_repo.pyandtest_repo_hygiene.py. Every test file among them was in these runs. No skips.test_repo_hygiene.py,test_readme_sections.pyandtest_readme_windows_quoting_sentence.py, 144 passed.tests.ymland the two template workflows: exit 0, with shellcheck on PATH.tests.ymlis the same blob as at 4c3b72a (5fc46de7); install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598 does not touch it.dirty=0):tests/test_repo_hygiene.py,tests/test_readme_sections.py,tests/test_cli_reference.py,tests/test_setup_by_hand_commands.pyandtests/test_readme_windows_quoting_sentence.py: 171 passed in 17.09s.tests/test_install_sh*.py(there is one) and every test filegit grep -l 'install.sh' tests/finds (test_install_sh.py,test_repo_hygiene.py; the other two hits areconftest.pyandreadme-cap.d/564.txt).test_repo_hygiene.py,test_readme_sections.pyandtest_readme_windows_quoting_sentence.py, 144 passed.tests.ymland the two template workflows: exit 0, with shellcheck on PATH. The workflow keeps main's .github/workflows/tests.yml: a push run on main is grouped by its commit, so the next landing no longer cancels it, and every landing gets its own Linux result (#592) #593concurrencyblock byte for byte (this PR's twotests.ymlhunks are the header and the new job), the header's "The FULL suite runs on LINUX ONLY" with the exception sentence, andtests-git-bash.dirty=0): the two suites,tests/test_repo_hygiene.py,tests/test_readme_sections.py,tests/test_cli_reference.pyandtests/test_setup_by_hand_commands.py: 281 passed in 61.24s.git-filter-repoon PATH: 113 passed;test_repo_hygiene.pyandtest_readme_sections.py, 141 passed.tests.ymland the two template workflows: exit 0, with shellcheck on PATH.pushtomain,pull_request,workflow_dispatch. Jobs:tests(ubuntu-latest) andtests-git-bash(windows-latest,timeout-minutes: 15). Neither has anif:, so both run on every trigger.test_setup_by_hand_commands.py, 276 passed. Lane 5's re-review got the same 276 there. On 649b089, withtests/test_setup_by_hand_commands.py,tests/test_agents_md_codes.pyandtests/test_gh_guard.pyas well, 334 passed.windows-latest, through probe branches that ran only these suites:Git\cmd): 113 passed.l5-585-probe-4, on 01e8c2c): 113 passed in 77.92s (run 38769166462).WINDOWS_SKIPlifted (probe 1): 88 failed, 23 passed. Two causes: the WSL launcher, and the real network throughGit\bin\bash.EXE.tests-git-bash: 113 passed in 50.28s, with the whole job taking 1 min 26 s (18:37:00Z to 18:38:26Z, check run 116601758252).C:\Program Files\Git\usr\bin\bash.exe.-q -rAand a 15-minute job timeout.Mutation probes
Probe 2 ran these on ebba347, the tree before the squash, which still had a
run_pipedhelper; thetext-stdinrow is that helper. The squashed head 649b089 was then re-measured whole by probe 3 (113 passed), and fix round 2's head 01e8c2c by probe 4. Each row reverts one Windows hunk and runs the two files again.BASHis the bare wordbashwrite_text(CRLF)test_a_release_whose_command_records_no_ref_exits_one, whose launcher's first line became#!/usr/bin/env bash\rand was refused as not the commandgit_bash_sessionbash -sscript sent as bytes (run_piped)Fix round 2's anchors (probe 4, on 01e8c2c):
native_path(ran_in)reverted toPath(ran_in)in both assertsran_dir, so this is the only way to revert it.ghshim test, its PATH joined with:instead ofos.pathsepauth status, and only the shim writes that log, so an unreachable shim would fail it.ln -scases withMSYS=winsymlinks:nativestrictexported, headMSYSdrop revertedln-defaultmade a link), 1 passedWith lane 3's #590 (
l3-563-pypi-package): the second to land converts four cases, and resolvestests.yml#590 adds four cases to these two suites, written while they skipped on Windows:
test_a_command_that_is_a_symlink_is_refused_before_any_request[live|dangling];test_install_refuses_a_target_that_is_a_symlink[live|dangling].Each compares a path bash printed with
str(path). Each also comparesos.readlink's answer with the target, and on Windows that answer typically starts\\?\(Python'sos.readlinkdocumentation, since 3.8; the probe's failures stop at the first assert, so no log shows it). Linux passes on the merged tree (117 passed), but under Git Bash those four cases fail, andtests-git-bashruns them.Whichever of #596 and #590 lands second applies these hunks to the merged tree. Probe 4 (run 38769166462) applied exactly these.
Both files already import
bash_pathonce #596 is in. On Linux, the merge of 649b089 with d090f0c, with these hunks: 117 passed. Fix round 7, on the merge of 52524fe with #590's head 1c8a708, with fix round 6's resolvedtests.yml(both inputs unchanged since; the result is blob 266c62c7): the hunks apply (git apply --checkexit 0). The two suites (117 by collection),tests/test_install_sh_filter_repo.py(18),tests/test_openreposhape_package.pyandtests/test_repo_hygiene.pygive 270 passed in 48.69s. Fix round 6: on the merge of 4c3b72a with #590's current head 1c8a708, withtests.ymlresolved as below, the hunks apply (git apply --checkexit 0) and the two suites give 117 passed; withtests/test_openreposhape_package.pyandtests/test_repo_hygiene.py, 252 passed. Probe 4 runs the merge of 01e8c2c withl3-563-pypi-packageon Windows: the four cases without the hunks, then both suites with them. In run 38769166462, on Windows:4 failed, 113 deselected. These are exactly the four cases. The launcher pair failed onREFUSED: D:\a\_temp\…\bin/openRepoShape is a symbol…, which confirms rv585's inferred launcher failures; the install pair failed onREFUSED: /d/a/_temp/…/.local/bin/openRepoShape ….tests.ymlconflicts with #590 (fix round 6; unchanged in fix round 7, where #590's head is still 1c8a708 and this PR'stests.ymlis the same blob). #590 now targetsmaintoo: at fix round 6 its merge ref's first parent was main 77bb5f0. Its head 1c8a708 has merged #587's final head and main, so it holds #597, and it merges with main cleanly. Both PRs add to the same two places intests.yml, so against 4c3b72a there are three regions:packagejob.tests-git-bash, then The openreposhape package: openRepoShape and four tool commands, run from a clone of the standard at the stamped commit and checked against its digest; --install and install.sh refuse a symlinked target (#563) #590'spackage.tests.ymlwith The openreposhape package: openRepoShape and four tool commands, run from a clone of the standard at the stamped commit and checked against its digest; --install and install.sh refuse a symlinked target (#563) #590's header paragraph and itspackagejob added. Against The openreposhape package: openRepoShape and four tool commands, run from a clone of the standard at the stamped commit and checked against its digest; --install and install.sh refuse a symlinked target (#563) #590's file it differs only in this PR's two hunks, and against this PR's file only in The openreposhape package: openRepoShape and four tool commands, run from a clone of the standard at the stamped commit and checked against its digest; --install and install.sh refuse a symlinked target (#563) #590's two.yaml.safe_loadgives the jobs['tests', 'tests-git-bash', 'package'], and actionlint 1.7.12 exits 0 on it. The openreposhape package: openRepoShape and four tool commands, run from a clone of the standard at the stamped commit and checked against its digest; --install and install.sh refuse a symlinked target (#563) #590'spackagecomment now says "the other jobs" and "on Linux like the rest of the suite since ci: run the test suite on Linux only #597", which stay true besidetests-git-bash. Fix round 4's advice to droptests-windowsandtests-macosand to reword "three jobs" no longer applies: The openreposhape package: openRepoShape and four tool commands, run from a clone of the standard at the stamped commit and checked against its digest; --install and install.sh refuse a symlinked target (#563) #590 has done both.Merge checks (
git merge-tree --write-tree, head 52524fe)The open pull requests are the 17 with a
refs/pull/<n>/mergeref ingit ls-remote origin 'refs/pull/*': 583, 590, 594, 596, 599, 614, 617-623 and 625-628. Each head was merged with 52524fe and, separately, with main 396c0ff. The ones not in the table merge cleanly with this head and touch none of its six files: #583, #614, #617, #618, #619, #621, #622, #625, #626 and #628.l3-563-pypi-package).github/workflows/tests.yml, 3 regions: the header and the end of the jobs. #590 against main alone is clean, so this is the two PRs' own overlap. It also touchestests/test_install_sh.pyandtests/test_openreposhape_command.py, which merge as text. See the #590 section above for the resolution, the four test cases and fix round 7's 270 passed..github/workflows/tests.yml, 2 regions. #599 also conflicts with main alone, in six files (tests.yml, README.md, the handbook,install.sh,openRepoShape,tests/test_install_sh.py). It is #597's conflict, through #599's stale base, not this job's.fix/606-readme-followups, #606; new since fix round 6)CONTRACTS_DIRand thesingle-repository.yamlquestion in § Adopting) merge as text with this PR's. #627 against main alone is clean. Whoever lands second sets the seven markers to the merged README's hash. On this head plus fc0656b that isfe0d932b031e4a08fc885f93d6cd978df0d5a25c. README is then 1456 lines: 1449 plus #627's ownreadme-cap.d/606.txt(+7).tests/conftest.py, which merges as text.adopt-project.py, and so does #620 against main alone: its own conflict with main, not an overlap with this PR.#598 landed as 3fb21d9 and is in the main row; fix round 7 is that merge. Every lane-5 PR and every other PR that has landed is in the main row.
For the squash message
The first commit's message says "
bash -sruns keepbashas argv[0] and nameBASHas the executable". That is true of the launcher's suite only. The installer's suite still passesBASHas argv[0], as before; install.sh reads neither$0norBASH_SOURCE. The squash message should say so; the fix-round-2 commit message already does. It should also nametests-git-bashand Brett Heap's word, and say the guard step now lives in that job. Since fix round 7 it should also say that README step 3'sinstall.shparagraph is reworded to hold both #598's text and the Git Bash clause at the 1449-line cap. The paragraph now opens "install.sh, attached to every release, installs the newest with noghand no login", and says "It never runssudoitself".Left out, and why
tests/test_setup_sh.py, andtest_repo_hygiene.py'sbash -ntest, still skip on Windows, and since ci: run the test suite on Linux only #597 nothing runs them on Windows at all. Git Bash on Windows: run the launcher and install.sh tests under Git Bash in the Windows job, so the one-line install is tested there (follow-up to #564) #585 names the launcher's and the installer's suites, and Brett Heap's word keeps a job for those two only. The sameBASHwould serve both.yes. CI has no terminal.tests/test_install_sh_filter_repo.pyintests-git-bash(fix round 7). install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's file is not added to the job; that is the coordinator's call. See checklist candidate 8.docs/release-notes/v1.0.0.mdanddocs/release-notes/v1.0.1.mdare records of those releases, and ci: run the test suite on Linux only #597 left them unedited too. v1.0.1's notes say Git Bash on Windows: run the launcher and install.sh tests under Git Bash in the Windows job, so the one-line install is tested there (follow-up to #564) #585 runs the suites under Git Bash; that stays true. The v1.1.0 text is below.tests-git-basha required status check. That is a ruleset setting, and Brett Heap's call.For the v1.1.0 release notes (lane 3 owns the notes)
No v1.1.0 notes file exists on main yet, so this PR writes none. Proposed text, a section of its own:
Checklist candidates
python3line on Windows.--install'smachine:block probes onlypython3. Where Python came from python.org, Git Bash haspythonand nopython3, or only the Microsoft Store stub. The block then sayspython3 MISSING, or "present" for a stub, while--preflightpasses withpython.$OPENREPOSHAPE_REPO.shape_clone_urltreatsC:/src/openRepoShapeas an owner/name and buildshttps://github.com/C:/src/openRepoShape.gitfrom it. Git Bash and git itself both accept the drive-letter spelling.$OPENREPOSHAPE_BIN_DIR.--installthen printsexport PATH="D:\...:$PATH", which bash cannot use: the drive's colon splits it.test_a_ref_the_doctor_would_check_out_is_validatedsays the refusal arrives before any clone.shape_checkoutclones first, then checks the ref.yes.curl … | bash, install.sh:342 (:277 before install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598) hands its children /dev/null for stdin.open_terminal()returns None whenos.name == "nt", so it has no Windows path to the person's terminal. OpeningCONIN$, where POSIX opens/dev/tty, might be the analogue.data-platform="linux mac wsl"(docs/handbook.html:775-784 on 01e8c2c), so a reader on the Windows tab never sees the one-line install that the Windows panel says Git Bash runs. Adding it to the Windows tab is a docs decision.openRepoShape --doctor, a familysetup-project.pyrun andfamily.py, started from Git Bash, run under native Windows Python, whereos.nameisnt. Soquote_arg,shell_quoted,shell_lineandshell_word(for example setup-project.py:2331,if os.name == "nt" and not posix) quote for PowerShell, even though the person is at a bash prompt. bump-leg.py and update-shape.py quote the paste lines that named a root or a path bare, the doctor'squote_argquotes a leading=for zsh, and README says the printed commands are for a POSIX shell (#532) #588's sentence stays true ("on Windows"). But someone who follows "Git Bash runs that command" and pastes such a line back into Git Bash gets the failure thattests/test_readme_windows_quoting_sentence.py's docstring names:git -C C:\work\Orbitreads asC:workOrbit. Choosingsh's dialect under Git Bash (for example onMSYSTEM) would be a Printed commands are POSIX-only: quote the bare paste lines in bump-leg.py and update-shape.py, and say in the docs that pasted commands are for a POSIX shell #532 decision.pip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's installer cases do not run under Git Bash.tests-git-bashrunstests/test_install_sh.py, and since fix round 7 that suite reaches install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's code in two ways: every run now printsadoption:anddone:, andno_pipxexpects uv's printed command. But the uv install, the readinesses' wording and the pasted next steps are tested intests/test_install_sh_filter_repo.pyonly, on Linux. README says Git Bash runs step 3's one line, "tested in CI".test_install_sh.pytestmark(BASH_SKIPandgit_bash_session), and drives every run throughNet:fake(LF bytes throughwrite_script),run(BASH,bash_path(INSTALLER)),serve,installedandcalls. Its ownuvfake writes LF bytes, likeNet.fake, and names the interpreter asshlex.quote(sys.executable), asNet.fakedoes in the job today. No assert compares a path bash printed with a Python path; each checks a line's start or end.PATH=net.pathalone, so the runner's own pipx or uv cannot leak in. The launcher's--installprobespython3withcommand -vand never runs it, so the loggingpython3/pythonfakes stay silent. Itspaste()sends a script through a text-mode pipe, asNet.run(piped=True)already does in the job.pip install git-filter-repofor adopting. README:768 (§ Adopting) says "git filter-repois a hard dependency —pip install git-filter-repo, or the same name from apt or brew." Installer: support uv for git-filter-repo and distinguish adoption readiness from general readiness #595 found thatpip installis refused on an externally managed Python (Debian 12, Ubuntu 23.04, Arch; PEP 668), and install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598's install.sh no longer prints it. Neither install.sh: git-filter-repo with the uv already there where there is no pipx, neverpip install --user, and two readinesses at the end,adoption:thendone:(#595) #598 nor this PR touches that line.Closes #585
🤖 Generated with Claude Code