Repository navigation
standard.py: the package's openRepoShape refuses a repository or ref holding U+202E, U+200B or another character the launcher refuses, by code point, before --version or any git call names it, and refuses a.lock/b, a/.b, a//b, a/b/ and a. before it clones, in the launcher's words (#939, #941) - #947
Conversation
…f holding U+202E, U+200B or another character the launcher refuses, by code point, before `--version` or any `git` call names it, and refuses `a.lock/b`, `a/.b`, `a//b`, `a/b/` and `a.` before it clones, in the launcher's words (#939, #941) #939: `clone_url` and `ref_fault` held a value to `CONTROL_RE` only, which holds nothing above U+009F. So `--version` printed a repository holding U+202E or U+200B as typed and exited 0, a ref holding either was refused with the value quoted, and a run that reached the clone handed `git` the URL with it. `standard.py` now carries the launcher's `MISDRAWN_CHARACTERS` as a table, entry for entry, and `misdrawn_characters`, which names what a value holds as `U+XXXX` in code point order. `clone_url` asks it first, on the repository as typed, and `ref_fault` asks it between the length and the control character: the launcher's order (`checked_repo`, `ref_character_fault`) and its two sentences. Both checks already stand before every line and every `git` call that names a value, so no run changes where it refuses. A run beside a checkout and `--preflight` still never read the values. #941: `ref_fault` kept the old shape line, which asked `..` and `.lock` of the whole name only, so a ref git refuses part by part cost a `git ls-remote`, a full clone and a failed `git checkout`. It now holds #874's rule (no `..`, no empty part, no part that starts with `.` or ends in `.lock`, no `.` at the end) and the launcher's sentence, word for word. Over the 3,110 names the issue measured, it disagrees with git on 0 (1,122 before). The docstring and the comment above `CONTROL_RE` state the five checks in their order and name the tests that hold the copy to the launcher. tests/test_openreposhape_package.py: the launcher's `UNPRINTABLE_VALUES` through `--version` under `C` and `C.UTF-8`, with cases that hold the order; the launcher's `NOT_CONTROLS` still printed; `--doctor`, a scaffold run, `shape-doctor` and `--shape-ref` refusing before any `git` call; the table held to the launcher's list and `SHARING_ITS_BYTES`; `REF_PARTS` against `ref_fault` and git; #874's five refs in `test_a_ref_is_checked_before_anything_is_cloned`; and the launcher's `test_help_answers_past_a_repository_and_ref_no_line_can_print`, imported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThis PR hardens repository and ref validation to match the launcher and git: visually ambiguous Unicode is rejected by code point before values can be printed or passed to git, and refs are validated component-by-component before cloning. Extensive parity and integration tests verify exact ordering, wording, locale behavior, no-leak guarantees, and zero external calls on refusal. Sequence diagram for preflight repository and ref validationsequenceDiagram
participant CLI
participant Prepare
participant Standard
participant Git
CLI->>Prepare: prepare()
Prepare->>Standard: target(ref)
Standard->>Standard: ref_fault(ref, what)
alt ref_fault returns refusal
Standard-->>Prepare: refusal
Prepare-->>CLI: exit 2 without value or git call
else ref passes
Prepare->>Standard: clone_url(repo)
Standard->>Standard: misdrawn_characters(repo)
alt repository is unsafe
Standard-->>Prepare: refusal
Prepare-->>CLI: exit 2 without value or git call
else repository passes
Prepare->>Git: clone URL
Git-->>Prepare: result
end
end
Flow diagram for ordered repository and ref checksflowchart TD
Start([Input repository or ref]) --> Kind{Value type}
Kind -->|Repository| RepoUnicode[Check misdrawn_characters]
RepoUnicode -->|Found| RepoReject([Refuse without echo or git])
RepoUnicode -->|None| RepoLegacy[Check leading dash or control character]
RepoLegacy -->|Invalid| RepoReject
RepoLegacy -->|Valid| RepoAccepted([Repository accepted])
Kind -->|Ref| RefEmpty[Check empty]
RefEmpty -->|Invalid| RefReject([Refuse])
RefEmpty -->|Valid| RefLength[Check length]
RefLength -->|Over limit| RefReject
RefLength -->|Valid| RefUnicode[Check misdrawn_characters]
RefUnicode -->|Found| RefReject
RefUnicode -->|None| RefControl[Check control character]
RefControl -->|Found| RefReject
RefControl -->|None| RefShape[Check REF_RE and component rules]
RefShape -->|Invalid| RefReject
RefShape -->|Valid| RefAccepted([Ref accepted])
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-10-18T17:08:26Z) RV947's independent review of #947 at
SHOULD-1. The body says the runs left alone never print a value or hand one to git. That is false beside a source tree with no
NITs:
Next: lane 3 runs a short fix round for SHOULD-1's text and NITs 1-4, and RV947 re-checks the delta. Then #947 lands under Brett Heap's v1.2.0 merge word, once Lane: openreposhape-3 (openRepoShape-3) |
… the repository's fault first, as the launcher does, and the ref table holds a lone `.` part (#939, #941) Fix round 1 of #947, after rv947's review (READY; one SHOULD, which is the pull request's text, and five NITs). rv947's NIT-1: `prepare` asked `target(stamp, typed_ref)` before `repository()` and `clone_url(repo)`, so with `$OPENREPOSHAPE_REPO` and `$OPENREPOSHAPE_REF` both bad, `--doctor`, a scaffold run and the four tools named the ref's fault. The launcher and the package's own `--version` name the repository's first, and #939's Direction says "the repository first". The two lines move above `target`; nothing between them reads the other (`target` reads the stamp, `--shape-ref` and `$OPENREPOSHAPE_REF`; the repository check reads `$OPENREPOSHAPE_REPO`), and the docstring of `prepare` says the order. rv947's NIT-3: `REF_PARTS` gains `("a/./b", False)`. Git, the launcher and the package refuse it, and a copy that took a part that is exactly `.` survived the table, because `a/.` is refused for ending in `.` as well. The comment above the table counts the names the old line took as fourteen. rv947's NIT-4: `test_version_prints_a_repository_holding_none_of_the_list` needs a UTF-8 stdout in the child, so it carries the launcher's `NO_NON_ASCII_INTO_GIT_BASH` skip on Windows, as the launcher's twin does. tests/test_openreposhape_package.py: `test_with_both_values_bad_a_run_names_the_repository_first`, for `--doctor`, a scaffold run and `shape-doctor`: a repository holding U+202E and a ref holding an escape give the repository's refusal and only it, no `git` call, neither value echoed (the launcher's `test_bad_values_are_refused_in_the_order_the_check_states`). The old order fails its three cases. A part that is exactly `.` taken fails `test_ref_fault_takes_a_ref_as_git_does_and_refuses_the_rest[a/./b]`, and the launcher's, `setup.sh`'s and `install.sh`'s tests read the same row. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-3, 2026-10-18T17:24:15Z) RV947 round 2, on the delta
NIT-6 (new, cosmetic): one line of #947 lands under Brett Heap's v1.2.0 merge word once Lane: openreposhape-3 (openRepoShape-3) |
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 3 days by commenting |
… holding U+202E or another character the launcher refuses, by code point and in the launcher's words, before it prints the value or runs `git clone` (#953) (#1043) `setup-project.py`'s self-bootstrap refuses an `$OPENREPOSHAPE_REPO` holding a character the launcher refuses (U+202E and the rest of `MISDRAWN_CHARACTERS`). It refuses by code point, in the launcher's words, before it prints the value or hands it to git. Head `c42afa30`, one commit on origin/main `5b41d44f`. Only `setup-project.py` and a new `tests/test_setup_project_repo_misdrawn.py` change. No flag, no `--help` line and no doc changes. **`setup-project.py`** (section 0, just above `self_bootstrap`): - `MISDRAWN_CHARACTERS` is the launcher's list (`openRepoShape:502` at `5b41d44f`), entry for entry, in the package's tuple form. It is a copy, for `checked_value`'s reason: self-bootstrap mode runs before there is a checkout to read the launcher from. - `misdrawn_characters(value)` names the characters a value holds as `U+XXXX`, in code point order. It is the package's `standard.misdrawn_characters`, and it looks for characters, as `CONTROL_RE` does. - `checked_repo(value)` asks that first. It refuses with the launcher's `checked_repo` sentence, which names the code points and nothing else of the value. Then it calls `checked_value(..., PATH_RE)`, unchanged, for a leading `-` and a control character. - `self_bootstrap` reads a non-empty `$OPENREPOSHAPE_REPO` through `checked_repo`. Unset or empty still means the default. The check stays where `--org` is asked, so the order is the same: `--org` first, then the repository, then `openRepoShape setup` is printed, then `mkdtemp` and `git clone`. - The value's raw bytes are never printed. The refusal goes through `die`, so it is `REFUSED: ...` on stderr with exit 2. **`tests/test_setup_project_repo_misdrawn.py`**, 22 tests: - **Parity:** `test_the_misdrawn_table_is_the_launchers_list` reads `MISDRAWN_CHARACTERS=(...)` out of `openRepoShape` and compares the tuple. It also checks that `misdrawn_characters` names all of the launcher tests' `MISDRAWN`, each once and in order. `test_misdrawn_characters_names_the_launchers_and_nothing_else` runs the launcher's `SHARING_ITS_BYTES`. These follow #947's two tests for the package. - **Direct path, 12 tests:** two runs (`Atlas --org acme`, which is the issue's run, and `--org acme` alone) times six values: U+202E, U+00AD, U+200B, U+E0001 (two, three and four UTF-8 bytes), two characters at once, and a tab before U+202E (the launcher asks for the code point before the control character). - Each test runs a lone copy of the file under `call_loggers`' logging git, gh and curl. - Each checks the launcher tests' `assert_refused_saying`: exit 2, the launcher's sentence, an empty stdout, and no listed character, escape or `forged` in either stream. So no `E2 80 AE` gets out. - The git log must hold exactly one call: the checkout probe. - **Values that still go on, 7 tests:** the launcher's `NOT_CONTROLS` after `acme/`: e-acute, a no-break space, U+200C and U+200D where a script or an emoji needs them, U+2026, U+FF21 and a subdivision flag. Each is printed in the clone line and handed to `git clone` as before. - **Package path, 1 test:** a tree with no `.git` (the package's modules under `src/openreposhape/`, plus `setup-project.py`, `scaffold-project.py`, `shape-doctor.py` and `contracts/repository-naming.yaml`). `openRepoShape Atlas --org acme` is run from that tree's `src` with the issue's value. The package prints its one line (`the standard is the checkout this package sits in, <tree>.`) and hands over. The tree's `setup-project.py` refuses: exit 2, the launcher's sentence, no U+202E and no `forged` in either stream, and only the checkout probe in the git log. - Tests that use the logging stub are skipped on Windows. There `subprocess` starts `git.exe` and never the stub's `.cmd`, so the stub would log nothing. CI is Linux only (#597). - `setup.sh` has no twin of these tests. Its guard covers control characters and the two separators and no other listed character, by #940's design: the launcher refuses the rest first where it is the way in. **"No git call", stated exactly.** The refused run makes one git call, `git -C <dir> rev-parse --show-toplevel`. That is `is_shape_checkout`, which runs before `self_bootstrap` on every run past `--help` and `--preflight`. It asks whether the file stands in a checkout, which is how the run knows it is self-bootstrapping at all, and it names nothing of the value. No git call names the value, and there is no clone. Moving the check above the probe would also refuse the value on the developer path, which never reads it. It would also leave `setup-project.py` ordered differently from `setup.sh`, which probes first and guards only on its self-bootstrap path. These run `$Q/v118/w953/probe.py`. Each case uses a fresh scratch folder, a logging git stub that then runs the real git, a gh that fails, `GH_TOKEN=bogus`, an empty `GH_CONFIG_DIR`, `GIT_SSH_COMMAND=false` and `GIT_CONFIG_GLOBAL=/dev/null`. "Before" is `git archive 5b41d44`; "after" is the worktree. Results are the same under git 2.43.0 and 2.47.1. | Case | Before (`5b41d44f`) | After | |---|---|---| | package, ZIP tree, `Atlas --org acme`, `acme/r[U+202E]forged` | exit 128; `E2 80 AE` on stdout and stderr; git: probe, then `clone --quiet -- acme/r[U+202E]forged /tmp/openreposhape-shape-...` | exit 2; no `E2 80 AE`; git: probe only; stdout is the package's one line | | direct, `Atlas --org acme`, same value | exit 128; raw bytes in the clone line; probe + clone | exit 2; refusal names `U+202E`; probe only | | direct, `--org acme`, same value | exit 128; probe + clone | exit 2; probe only | | direct, `acme/r[U+200B]forged` | exit 128; probe + clone | exit 2; names `U+200B` | | direct, `acme/r[U+E0001]forged` | exit 128; probe + clone | exit 2; names `U+E0001` | | direct, `acme/r[U+2026]b` (not listed) | exit 128; probe + clone | the same: probe + clone, exit 128 | The "after" refusal, from the probe: ``` REFUSED: OPENREPOSHAPE_REPO holds a character a terminal draws as something other than itself (U+202E): one that is invisible, is drawn as another character, ends a line or reorders the text around it. This tool will not put it on a `git clone` command line. Set it to a URL or a path. ``` The new file was run on main's `setup-project.py` (`git show 5b41d44:setup-project.py`, put back byte for byte afterwards): **15 failed, 7 passed**. - Failed: both parity tests (`AttributeError`: no `MISDRAWN_CHARACTERS`, no `misdrawn_characters`), all 12 direct tests, and the package test. - Passed: the 7 `NOT_CONTROLS` tests. They pass at main by design, because they guard against a check that is too broad. Each mutant was applied alone (`$Q/v118/w953/mutate.py`), the new file was run (22 tests), and both files were put back. **All 18 are killed.** | Mutant | Failed | |---|---| | m01 check removed (main's read) | 13 | | m02a check moved after the `(0) self-bootstrap` line | 13 | | m02b check moved after the clone line | 13 | | m03a only the value's first character checked | 15 | | m03b only the table's first entry checked | 13 | | m04 only U+202E checked | 8 (U+00AD, U+200B, U+E0001, two-at-once) | | m05 refusal exits 1 | 13 | | m06a table drops `E0001` | 4 | | m06b parity compares the first five entries + table drops `E0001` | 4 (parity's second assert, `SHARING_ITS_BYTES`, both U+E0001 tests) | | m06c parity compares the first five + table adds `200C` | 2 (`SHARING_ITS_BYTES`, the Persian/U+200C test) | | m06d parity compares the first five + `2060-206F` narrowed to `206E` | 2 | | m06e parity uses set inclusion + table drops `E0001` | 4 | | m07 a range read without its last code point | 15 | | m08 control check asked before the code points | 2 (both tab-then-U+202E tests) | | m09 characters named in the value's order | 3 | | m10 every character past ASCII refused | 7 (every `NOT_CONTROLS` test) | | m11 refusal quotes the value back | 13 | | m12 check skipped on a hand-over (`OPENREPOSHAPE_SELF_BOOTSTRAP=1`) | 1 (the package test alone) | All at `c42afa30`, one pytest process at a time: `/usr/bin/python3 -m pytest -q -p no:cacheprovider -o tmp_path_retention_policy=failed`, one reused basetemp, `EMAIL=service@opensoft.dev`, a stub gh first on PATH, `GH_TOKEN=bogus`, an empty `GH_CONFIG_DIR`, `GIT_SSH_COMMAND=false`, `GIT_CONFIG_GLOBAL=/dev/null`. | Run | git | Files | Result | |---|---|---|---| | h1 | 2.43.0 | `test_setup_project_repo_misdrawn.py` (22), `test_setup_project_ref_parts.py` (27), `test_openreposhape_package.py` (213), `test_readme_windows_quoting_sentence.py` (44), `test_release_install_check_children.py` (30), `test_repo_hygiene.py` (100), `test_agents_md_codes.py` (47), `test_cli_reference.py` (23) | 506 passed in 94.06s | | h2 | 2.43.0 | `test_setup_project_py.py` | 340 passed in 122.68s | | h3 | 2.43.0 | `test_setup_sh.py` | 191 passed in 96.62s | | h4 | 2.47.1 | `test_setup_project_repo_misdrawn.py`, `test_setup_project_ref_parts.py`, `test_setup_project_py.py` | 389 passed in 128.13s | | h5 | 2.43.0 | `test_no_auto_maintenance.py`, `test_setup_project_dry_run_offers.py`, `test_validator_child_encoding.py`, `test_setup_project_name_edges.py`, `test_setup_project_typed_name.py` | 353 passed, 2 skipped in 73.92s (the two skips: `GIT_CONFIG_GLOBAL` was set) | No `shell_quoted` call was added (count 0), so the README's and the handbook's Windows-quoting sentences and `NAMED` are unchanged. `setup-project.py` is not a pinned copy. The wheel takes it at build time (`pyproject.toml` `force-include`), and the tests that read its source (`test_setup_project_py.py`, `test_release_install_check_children.py`, `test_repo_hygiene.py`) pass. - `git merge-tree --write-tree` of `c42afa30` with origin/main `5b41d44f` (fetched with `-c gc.auto=0 -c maintenance.auto=false`) is clean, tree `59de4c261c228274a5cff18aff953211de7002e3`. - No open PR touches `setup-project.py` or the new test file. Every open PR's head was still merged in, and all ten are clean: #1041 `4a23c6e2`, #1039 `5f924500`, #1038 `fd1de5ab`, #1031 `eea70be1`, #1030 `9b4845bb`, #1028 `0c9eac6c`, #1027 `052f1e24`, #1025 `f83ca618`, #1023 `4d0aa1cf`, #954 `dc7fd7ea`. - **RV947's companion is left out.** That companion is the package's scaffold run asking the repository even beside a checkout. - Once this lands, no package run prints the value or hands it to git. Beside a git checkout, the hand-over takes `setup-project.py`'s developer path, which never reads `$OPENREPOSHAPE_REPO`. `self_bootstrap` and `standard.py` are the only shipped Python that read it. Beside a tree with no `.git`, this check refuses it. - The companion would change `standard.prepare`, which `--doctor` and the four tools share. They would then refuse a value they never use. That is the launcher's "checked once, before every run but `--help`" policy, and it is a decision about the package of its own. - `src/openreposhape/standard.py` is also open in #1038. - **Found while measuring, not changed here:** `CONTROL_RE` and `PATH_RE` hold `\x00-\x1f\x7f` only, so a C1 control passes `checked_value`. The launcher and the package refuse C1 (U+0080 to U+009F) in a repository. With `acme/r[U+009B]31mforged`, the clone line prints the raw `C2 9B`, before and after this change. `--into` and `--local-remote-dir` go through the same `PATH_RE` (read in the code, not measured). That would be a follow-up for `checked_value`, which every flag shares. Review: rv953, lane 5's independent reviewer, not the writer (w953): READY at `c42afa30`, the head that lands (issuecomment-6284626889), with no MUST. It rules #953's Verify met in substance: the refused run's only git call is the checkout probe every run makes, which names nothing of the value, and there is no clone. Its SHOULD is #459's documented C1 known limit (U031, L4-24), left with its test NIT for a follow-up on a ruling. CI (Brett Heap's ruling 27): H0 is the head c42afa3 itself, which passed tests, tests-git-bash and SonarCloud; main has touched none of its files since its base 5b41d44; and the local gate passed on its predicted squash tree e146bb5 over main e656199: the setup_project_repo_misdrawn, repo_hygiene, agents_md_codes, cli_reference sets, 192 passed. Landed on Brett Heap's word of 2026-10-18T14:45:28Z, relayed to every lane by lane 3 (#473 issuecomment-6251255596): "merge v1.2.0 PRs once reviewed and green". Closes #953. Lane: openreposhape-5 (openRepoShape-5) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Closes #939.
Closes #941.
Lane 2 filed both issues and suggested pairing them, because they share
standard.py'sref_fault. One commit covers both, since each edits the same docstring and the same comment.cli.pyis unchanged: the checks stand instandard.clone_urlandstandard.target, which--version(cli.print_version) andpreparealready call before any line orgitcall names a value.Where the rules come from (the launcher,
openRepoShapeat64464d31)MISDRAWN_CHARACTERS(:501) andmisdrawn_characters(:512): the 41 code points, named asU+XXXX, comma-separated, in code point order.checked_repo(:805): drawn as another, on the value as typed; then a leading-or a control character.ref_fault(:882, shape line:895) withref_character_fault(:908): empty, length, drawn as another, control character, shape.*..* | *.lock), so a ref git refuses part by part (a.lock/b,a/.b,a//b) clones the standard before it fails #874's rule and sentence, the same bytes insetup.shandinstall.sh.tests/conftest.pystates it asREF_PARTSandREF_RULE_WORDS.#939: what changed, by section
--versionprinted a repository holding U+202E or U+200B as typed, exit 0.standard.pynow carries the launcher's list asMISDRAWN_CHARACTERS, entry for entry, andmisdrawn_characters, which names what a value holds.clone_urlasks it first, on the repository as typed, and refuses inchecked_repo's words. Then comes its old check, a leading-or a control character.--versioncallsclone_urlbefore it prints, so it now exits 2 with the launcher's sentence and no byte of the value.ref_faultasksmisdrawn_charactersbetween the length and the control check, where the launcher asks it, inref_character_fault's words. The shape refusal, which quotes the ref, is reached only by a value that passed all four checks.gitthe URL.preparecallstargetandclone_urlbefore it looks forgit, so--doctor, a scaffold run,--shape-refand the four tools refuse before anygitcall. Measured by hand on a stamped copy with a logginggitfirst onPATH, for--doctor .,Atlas --org acmeandshape-doctor --json: a repository with U+202E, a ref with U+202E and the refa.lock/beach made 0gitcalls, with noE2 80 AEon either stream. A cleanacme/rmade 1 call (git clone).install.sh's does. That also keeps the package pure ASCII, whichtest_the_package_is_pure_asciirequires.ref_fault's docstring and the comment aboveCONTROL_REnow state the five checks in order and name the tests that hold the copy to the launcher.--preflightdo not refuse either value. Neither run prints a value or hands one togit(prepareandprint_versionreturn first beside a git checkout, and--preflightreturns beforeprepare), so the narrower direction refuses nothing that could reach a line there. A third run reads only half: a scaffold run with a clean--shape-refnever asks$OPENREPOSHAPE_REFanything (targetreturns the typed ref first), so that value is never printed or sent, though the launcher refuses it. A check inmain_openreposhapewould also have to be repeated inrun_toolfor the four tools..git, such as a ZIP, or an sdist installed withpip install -e ..beside_checkoutchecks for files only, so that tree'ssetup-project.pyself-bootstraps and hands$OPENREPOSHAPE_REPOtogit clonewithout the drawn-as-another check (checked_value,setup-project.py:881-882). That is the same at origin/main, so this PR does not regress it. It is now setup-project.py: under self-bootstrap, an$OPENREPOSHAPE_REPOholding U+202E or another character the launcher refuses is printed and handed togit clone, because its check has no drawn-as-another test #953.#941: what changed, by section
*..* | *.lock), so a ref git refuses part by part (a.lock/b,a/.b,a//b) clones the standard before it fails #874's rule:REF_RE, no.., no empty part (//, or a/at the end), no part that starts with.or ends in.lock, and no.at the end. That issetup-project.py'swhy_not_a_refrule. I measured the issue's 3,110 names (every name of up to five pieces froma,0,.,/,-,.lockthatREF_REtakes). This rule disagrees withgit check-ref-format --branchon 0 of them, and with the launcher's ownref_fault, lifted out and run by bash, on 0. origin/main's line disagrees with each on 1,122.a.lock/b,a/.b,a//b,a/b/anda.are now refused bytarget, beforeremote_commitand the clone: 0gitcalls (see the tests below).REF_RULE_WORDS).--shape-refgets the same sentence with its own opening.test_ref_fault_takes_a_ref_as_git_does_and_refuses_the_restis now the fourth reader ofREF_PARTS. It runs in-process withassert_a_copy_answers_as_git_does("standard.py", ...)andassert_the_refusal_states_the_ref_rule, and git is asked about each name, never a repository.Tests added (
tests/test_openreposhape_package.py), 115 casestest_version_refuses_a_repository_or_ref_no_line_can_print(34): the launcher's 12UNPRINTABLE_VALUESand 5 order rows, each underCandC.UTF-8, throughcmd.detached("--version"), checked withassert_refused_saying. The order rows are an escape then U+202E (repository and ref), U+202E then U+200B (namedU+200B, U+202E, repository and ref), and a 259-character ref holding U+202E (refused for its length).test_version_prints_a_repository_holding_none_of_the_list(14): the launcher'sNOT_CONTROLSprint as typed, exit 0, under both locales.test_a_run_that_clones_refuses_the_value_before_any_git_call(21):--doctor ., a scaffold run andshape-doctor, times a repository with U+202E, a ref with U+202E and setup.sh, install.sh and the openRepoShape command: each keeps its own copy of the old ref rule (*..* | *.lock), so a ref git refuses part by part (a.lock/b,a/.b,a//b) clones the standard before it fails #874's five refs, with a logginggit,ghandcurlfirst onPATH: no call, and that one refusal.test_a_typed_shape_ref_is_refused_before_any_git_call(2):--shape-refwith U+202E, and witha.lock/b.test_the_misdrawn_table_is_the_launchers_list(1) andtest_misdrawn_characters_names_the_launchers_and_nothing_else(1, overSHARING_ITS_BYTES).test_ref_fault_takes_a_ref_as_git_does_and_refuses_the_rest(36,REF_PARTS).test_a_ref_is_checked_before_anything_is_cloned: plus setup.sh, install.sh and the openRepoShape command: each keeps its own copy of the old ref rule (*..* | *.lock), so a ref git refuses part by part (a.lock/b,a/.b,a//b) clones the standard before it fails #874's five refs (5).test_help_answers_past_a_repository_and_ref_no_line_can_print, the launcher's, imported unchanged (1).call_loggersis factored out oftest_a_bare_run_asks_nothing_of_git_gh_or_curlso the new tests share its fakes. That test's behaviour is unchanged.Left out of scope
openRepoShape --versionprints a repository holding U+202E or U+200B as typed and exits 0, and a ref holding either is refused with the value as typed, where the launcher refuses both before it prints (#866) #939's "Found while measuring, not this issue's" finding is in another file:setup.sh's--shape-refcheck has no drawn-as-another check, and quotes the value underC.UTF-8.prepareasked the ref (target) before the repository (clone_url). The launcher'srefuse_bad_valuesand the package's own--versionask the repository first, and openreposhape package:openRepoShape --versionprints a repository holding U+202E or U+200B as typed and exits 0, and a ref holding either is refused with the value as typed, where the launcher refuses both before it prints (#866) #939's Direction says "the repository first". It now asks the repository first: see Fix round 1 below.ref_faultkeeps the old ref rule, so it takesa.lock/b,a/.b,a//b,a/b/anda., clones the standard in full and fails atgit checkout, where the launcher, setup.sh and install.sh refuse them first (#874) #941. The package's--versionrefuses a ref the shape refuses (a.lock/b, as it already refusedtopic.lock). The launcher's--versionasks a ref only its characters (openRepoShape:--versionand two fetch refusals print$OPENREPOSHAPE_REPOand$OPENREPOSHAPE_REFwith no check, so an escape or a bidi override in either reaches the terminal as typed, and a scaffold run sends its fetch with them #866) and prints it.Clocale a$OPENREPOSHAPE_REPOURL or path, or a ref, with a letter past ASCII is refused as "a control character", so--version,--installand--preflightrefuse what v1.1.0 ran #940 and openRepoShape:--doctor'sshape_checkoutrunsgit clonebeforechecked_ref, so a ref it refuses (a.lock/b,a//b, one over 255 characters) costs a whole clone first, and where the clone fails the ref's refusal is never reached #942, lane 2's, are the launcher and are not touched. This module imports the launcher tests'UNPRINTABLE_VALUES,NOT_CONTROLS,MISDRAWN,SHARING_ITS_BYTES,LOCALES,ESCAPE,RLO,assert_refused_saying,repo_drawn_as_anotherandref_drawn_as_another, and readsMISDRAWN_CHARACTERS=(...)out ofopenRepoShape. A launcher PR that renames or rewords those will fail here too, which is the parity working.Test runs
Each run was a separate pytest process, one at a time, with a stub
gh(exit 97) first onPATH,GH_TOKEN=bogus, an emptyGH_CONFIG_DIRandGIT_SSH_COMMAND=false:timeout 2400 /usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-<name> -o tmp_path_retention_policy=failed <file>tests/test_openreposhape_package.pyat origin/main (64464d31):58 passed in 20.37stests/test_openreposhape_package.pyat this head (21558e44):173 passed in 28.66stests/test_openreposhape_command.py:447 passed in 46.44stests/test_agents_md_codes.py:47 passed in 1.75stests/test_cli_reference.py:23 passed in 136.99s (0:02:16)tests/test_update_add_codes_documented.py:6 passed in 0.48stests/test_repo_hygiene.py:81 passed in 3.41stests/test_no_auto_maintenance.py:60 passed in 12.58stests/test_validator_child_encoding.py(it namescli.py):163 passed in 71.79s (0:01:11)tests/test_release_workflow.py(it namessrc/openreposhape/):56 passed in 3.88stests/test_setup_sh.py(aREF_PARTSreader, unchanged):125 passed in 88.01s (0:01:28)tests/test_install_sh.py(aREF_PARTSreader, unchanged):270 passed in 40.76stests/test_shape_doctor.py: not run, because neithershape-doctor.pynor a helper it imports changed.No new refusal code was added (the package's refusals carry none), so no code doc changes. No doc quoted the old ref sentence: at origin/main,
git grepfinds it only instandard.py.Mutants
Each mutant was applied to
standard.pyand run against the whole oftests/test_openreposhape_package.py. Each is killed.standard.py--shape-refcases, the 2 table tests, 19REF_PARTSrows and the 5 added refs. The control rows, the length row,NOT_CONTROLS, the help case and the 17 rows git takes pass there as well, as they shouldclone_urlwithout the drawn-as-another checktest_version_refuses_...[repo-u+202e-C],test_a_run_that_clones_...[doctor-repo-u+202e]ref_faultwithout ittest_version_refuses_...[ref-u+202e-C],test_a_typed_shape_ref_...[u+202e]ref_faultasks the control check firsttest_version_refuses_...[ref-an-escape-then-u+202e-*]clone_urlasks the control check firsttest_version_refuses_...[repo-an-escape-then-u+202e-*]test_the_misdrawn_table_is_the_launchers_list,...[repo-u+200b-C]test_misdrawn_characters_names_the_launchers_and_nothing_elsetest_version_refuses_...[repo-two-at-once-*],[ref-two-at-once-*]test_version_refuses_...[ref-over-long-and-u+202e-*]test_ref_fault_takes_...[a.lock/b]and 12 other rows, the 5 refs in each runtest_ref_fault_takes_...[a//b],[a/b/].test_ref_fault_takes_...[a/.b],[a/b/.c/d].lockasked of the end of the name onlytest_ref_fault_takes_...[a.lock/b],[release.lock/v1].test_ref_fault_takes_...[a.],[a/b.].refused (over-refusal)test_ref_fault_takes_...[a./b]test_ref_fault_takes_...[Feature/ABC-123]_or-refused (over-refusal)test_ref_fault_takes_...[fix/_tmp],[fix/-wip]REF_PARTSrow, every shape refusal in a runMerges
git merge-tree --write-treeof this head with origin/main (64464d31): clean, treef4a9282699321be04716eaf639a234dd4cd8261d, exit 0.src/openreposhape/standard.py,src/openreposhape/cli.pyortests/test_openreposhape_package.py, so there was nothing else to merge with.Fix round 1 (head
c1de4c86, after rv947's review of21558e44)rv947 found
21558e44READY: one SHOULD, which is this text, and NITs. The sections above describe21558e44; this round changes the head as follows. One commit, 3 files, 38 insertions and 10 deletions..git(a ZIP, or an sdist installed withpip install -e .), that tree'ssetup-project.pyself-bootstraps and hands$OPENREPOSHAPE_REPOtogit clonewithout the drawn-as-another check. That is the same at origin/main, and it is now setup-project.py: under self-bootstrap, an$OPENREPOSHAPE_REPOholding U+202E or another character the launcher refuses is printed and handed togit clone, because its check has no drawn-as-another test #953.--shape-refnever asks$OPENREPOSHAPE_REF.standard.py'sprepare,repo = repository()andurl = clone_url(repo)now come beforewanted = target(stamp, typed_ref), with a comment and a docstring line that say the order. Nothing between them depended on the old order:clone_urlreads only$OPENREPOSHAPE_REPO, andtargetreads the stamp, the typed--shape-refand$OPENREPOSHAPE_REF. With both values bad,--doctor, a scaffold run and the tools now name the repository's fault first, as the launcher and the package's--versiondo.test_with_both_values_bad_a_run_names_the_repository_firstintests/test_openreposhape_package.py, mirroring the launcher'stest_bad_values_are_refused_in_the_order_the_check_states. It runs--doctor .,Atlas --org acmeandshape-doctor --json(3 cases) with a repository holding U+202E and a ref holding an escape, and wants exit 2,REFUSED: OPENREPOSHAPE_REPO holds ..., no mention ofOPENREPOSHAPE_REF, nogitcall and neither value echoed.prepare. It fails the 3 cases ([doctor],[scaffold]and[shape-doctor]) and nothing else:3 failed, 174 passed.("a/./b", False)is inREF_PARTSintests/conftest.py, and the comment above the table counts the names the old line took as fourteen.git check-ref-format --branch a/./banswersfatal: 'a/./b' is not a valid branch name. The four files that readREF_PARTS(package, launcher,test_setup_sh.py,test_install_sh.py) each gain one case, and all pass..is taken (rv947's own2). Before this round it survived, with 173 passed. With the new row deselected it still survives here (176 passed, 1 deselected, the 176 including the 3 new cases above). With the row, it failstest_ref_fault_takes_a_ref_as_git_does_and_refuses_the_rest[a/./b]and nothing else:1 failed, 176 passed.test_version_prints_a_repository_holding_none_of_the_listcarriesNO_NON_ASCII_INTO_GIT_BASH, imported from the launcher's tests, as the launcher's twintest_version_prints_a_path_a_utf_8_locale_printsdoes: it needs a UTF-8 stdout in the child, so it is skipped on Windows.Clocale a$OPENREPOSHAPE_REPOURL or path, or a ref, with a letter past ASCII is refused as "a control character", so--version,--installand--preflightrefuse what v1.1.0 ran #940 adds rows toNOT_CONTROLSor another imported table.Test runs at
c1de4c86, each a separate pytest process under the stubgh,GH_TOKEN=bogus, an emptyGH_CONFIG_DIRandGIT_SSH_COMMAND=false, wrapped intimeout 2400:tests/test_openreposhape_package.py:177 passed in 23.31s(173, the order test's 3 and the new row)tests/test_openreposhape_command.py:448 passed in 36.76stests/test_setup_sh.py:126 passed in 69.01s (0:01:09)tests/test_install_sh.py:271 passed in 27.20stests/test_repo_hygiene.py:81 passed in 1.84stests/test_no_auto_maintenance.py:60 passed in 5.79sMerges:
git merge-tree --write-treeofc1de4c86with origin/main (64464d31) is clean, tree0c09d9510683a514d5fa6ef2856b82928de35a4e. With #949's head (8c5d6051), which also editstests/conftest.py, it is clean too, tree1951cc0bbca9ed5634ee8c0a2d2b521310b42660.Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code