Repository navigation
setup-project.py: the self-bootstrap refuses an $OPENREPOSHAPE_REPO 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
Conversation
… holding U+202E, U+200B or another character the launcher refuses, by code point and in the launcher's words, before it prints the value or hands it to `git clone`, as a downloaded copy and the package's `openRepoShape` beside a tree with no `.git` both reach it (#953) #953: the self-bootstrap held `$OPENREPOSHAPE_REPO` to `checked_value(..., PATH_RE)`, which refuses a leading `-` and a control character and takes every character of the launcher's `MISDRAWN_CHARACTERS`. So `acme/r`, U+202E, `forged` was printed raw in the clone line (`E2 80 AE`) and handed to `git clone`, which exited 128. A downloaded `setup-project.py` reaches it (`python3 setup-project.py --org acme`), and so does the package's `openRepoShape` beside a source tree with no `.git` (a ZIP, or an sdist installed with `pip install -e .`): `beside_checkout` looks for files only, so the package hands over to that tree's `setup-project.py`, which finds no checkout and self-bootstraps. `setup-project.py` now carries the launcher's list as `MISDRAWN_CHARACTERS`, entry for entry, and `misdrawn_characters`, as the package does. `checked_repo` asks it first and refuses in the launcher's words, naming each character by its code point; `checked_value` then asks what it asked before. Both run before the first line the self-bootstrap prints and before the clone, so the run exits 2 and prints nothing of the value. The one git call before them is `is_shape_checkout`'s, which asks whether the file stands in a checkout and names nothing of the value. tests/test_setup_project_repo_misdrawn.py: the copy held to the launcher's list and to `SHARING_ITS_BYTES`; the issue's run and `--org acme` alone refused, for U+202E, U+00AD, U+200B, U+E0001, two at once and a tab before U+202E, under a logging git stub that sees only the checkout probe; the launcher's `NOT_CONTROLS` cloned as before; and the package's `openRepoShape` beside a tree with no `.git` refused the same way. The package's own scaffold run still does not ask the repository beside a checkout (RV947's companion): beside a git checkout no run reads the value, and beside a tree with no `.git` this check refuses it. Closes #953 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 1 day and 13 hours by commenting |
Reviewer's GuideSelf-bootstrap now rejects OPENREPOSHAPE_REPO values containing launcher-defined misdrawn characters by code point, using launcher-compatible diagnostics before printing or cloning, while retaining existing validation and allowing other Unicode values. New tests verify launcher parity, sanitized output, git-call behavior, direct and package bootstrap paths, and regression compatibility. Sequence diagram for safe repository self-bootstrap validationsequenceDiagram
participant Launcher as setup-project.py
participant Git as git
participant Output as stdout/stderr
Launcher->>Git: is_shape_checkout()
Git-->>Launcher: checkout probe result
Launcher->>Launcher: checked_repo(OPENREPOSHAPE_REPO)
alt misdrawn character found
Launcher->>Output: die(launcher's refusal with U+ code points)
Output-->>Launcher: exit 2
else value is valid
Launcher->>Output: say(openRepoShape setup)
Launcher->>Git: git clone --quiet -- repository target
end
Flow diagram for OPENREPOSHAPE_REPO validationflowchart TD
A[Read OPENREPOSHAPE_REPO] --> B{Value empty or unset?}
B -->|Yes| C[Use DEFAULT_SHAPE_REPO]
B -->|No| D[checked_repo]
D --> E[misdrawn_characters]
E --> F{Misdrawn characters found?}
F -->|Yes| G[die with U+ code points]
G --> H[Exit 2 without printing value or cloning]
F -->|No| I[checked_value with PATH_RE]
I --> J{Existing validation passes?}
J -->|No| K[Refuse value]
J -->|Yes| L[Print setup line and run git clone]
File-Level Changes
Assessment against linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-5, 2026-10-20T05:57Z) @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. |
|
Codex Review: Didn't find any major issues. Chef's kiss. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Independent review of #1043 (#953) at No MUST. One SHOULD, and it is a follow-up issue that does not block this PR. One NIT about a gap in the tests. Head FindingsSHOULD-1 (file a follow-up issue; do not fix it here). A C1 control in
NIT-1. No refused value in the tests starts or ends with a listed character. In
Rulings on the focus items1. The issue's Verify says "makes no git call"; the refused run still makes the checkout probe. Keep it. No code change.
2. The list copy.
3. The refusal.
4. What must still pass, and which message wins.
5. The C1 gap is real. See SHOULD-1: a follow-up issue. 6. The writer was right to leave out RV947's companion.
Checked and true
Could not verify
Checklist candidates
Reviewer: rv953, lane 5's independent reviewer, not the writer (w953). Lane: openreposhape-5 (openRepoShape-5) |



Lane: openreposhape-5 (openRepoShape-5)
Claim: #953 (comment)
Closes #953.
Goal
setup-project.py's self-bootstrap refuses an$OPENREPOSHAPE_REPOholding a character the launcher refuses (U+202E and the rest ofMISDRAWN_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/main5b41d44f. Onlysetup-project.pyand a newtests/test_setup_project_repo_misdrawn.pychange. No flag, no--helpline and no doc changes.What changed
setup-project.py(section 0, just aboveself_bootstrap):MISDRAWN_CHARACTERSis the launcher's list (openRepoShape:502at5b41d44f), entry for entry, in the package's tuple form. It is a copy, forchecked_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 asU+XXXX, in code point order. It is the package'sstandard.misdrawn_characters, and it looks for characters, asCONTROL_REdoes.checked_repo(value)asks that first. It refuses with the launcher'schecked_reposentence, which names the code points and nothing else of the value. Then it callschecked_value(..., PATH_RE), unchanged, for a leading-and a control character.self_bootstrapreads a non-empty$OPENREPOSHAPE_REPOthroughchecked_repo. Unset or empty still means the default. The check stays where--orgis asked, so the order is the same:--orgfirst, then the repository, thenopenRepoShape setupis printed, thenmkdtempandgit clone.die, so it isREFUSED: ...on stderr with exit 2.tests/test_setup_project_repo_misdrawn.py, 22 tests:test_the_misdrawn_table_is_the_launchers_listreadsMISDRAWN_CHARACTERS=(...)out ofopenRepoShapeand compares the tuple. It also checks thatmisdrawn_charactersnames all of the launcher tests'MISDRAWN, each once and in order.test_misdrawn_characters_names_the_launchers_and_nothing_elseruns the launcher'sSHARING_ITS_BYTES. These follow standard.py: the package'sopenRepoShaperefuses a repository or ref holding U+202E, U+200B or another character the launcher refuses, by code point, before--versionor anygitcall names it, and refusesa.lock/b,a/.b,a//b,a/b/anda.before it clones, in the launcher's words (#939, #941) #947's two tests for the package.Atlas --org acme, which is the issue's run, and--org acmealone) 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).call_loggers' logging git, gh and curl.assert_refused_saying: exit 2, the launcher's sentence, an empty stdout, and no listed character, escape orforgedin either stream. So noE2 80 AEgets out.NOT_CONTROLSafteracme/: 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 togit cloneas before..git(the package's modules undersrc/openreposhape/, plussetup-project.py,scaffold-project.py,shape-doctor.pyandcontracts/repository-naming.yaml).openRepoShape Atlas --org acmeis run from that tree'ssrcwith 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'ssetup-project.pyrefuses: exit 2, the launcher's sentence, no U+202E and noforgedin either stream, and only the checkout probe in the git log.subprocessstartsgit.exeand never the stub's.cmd, so the stub would log nothing. CI is Linux only (ci: run the test suite on Linux only #597).setup.shhas no twin of these tests. Its guard covers control characters and the two separators and no other listed character, by openRepoShape, setup.sh and install.sh: under theClocale 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'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 isis_shape_checkout, which runs beforeself_bootstrapon every run past--helpand--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 leavesetup-project.pyordered differently fromsetup.sh, which probes first and guards only on its self-bootstrap path.Probes, before and after
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 emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=falseandGIT_CONFIG_GLOBAL=/dev/null. "Before" isgit archive 5b41d44f; "after" is the worktree. Results are the same under git 2.43.0 and 2.47.1.5b41d44f)Atlas --org acme,acme/r[U+202E]forgedE2 80 AEon stdout and stderr; git: probe, thenclone --quiet -- acme/r[U+202E]forged /tmp/openreposhape-shape-...E2 80 AE; git: probe only; stdout is the package's one lineAtlas --org acme, same valueU+202E; probe only--org acme, same valueacme/r[U+200B]forgedU+200Bacme/r[U+E0001]forgedU+E0001acme/r[U+2026]b(not listed)The "after" refusal, from the probe:
Tests that fail at main
The new file was run on main's
setup-project.py(git show 5b41d44f:setup-project.py, put back byte for byte afterwards): 15 failed, 7 passed.AttributeError: noMISDRAWN_CHARACTERS, nomisdrawn_characters), all 12 direct tests, and the package test.NOT_CONTROLStests. They pass at main by design, because they guard against a check that is too broad.Mutants
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.(0) self-bootstraplineE0001E0001SHARING_ITS_BYTES, both U+E0001 tests)200CSHARING_ITS_BYTES, the Persian/U+200C test)2060-206Fnarrowed to206EE0001NOT_CONTROLStest)OPENREPOSHAPE_SELF_BOOTSTRAP=1)Test runs
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 emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=false,GIT_CONFIG_GLOBAL=/dev/null.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)test_setup_project_py.pytest_setup_sh.pytest_setup_project_repo_misdrawn.py,test_setup_project_ref_parts.py,test_setup_project_py.pytest_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.pyGIT_CONFIG_GLOBALwas set)No
shell_quotedcall was added (count 0), so the README's and the handbook's Windows-quoting sentences andNAMEDare unchanged.setup-project.pyis not a pinned copy. The wheel takes it at build time (pyproject.tomlforce-include), and the tests that read its source (test_setup_project_py.py,test_release_install_check_children.py,test_repo_hygiene.py) pass.Merges
git merge-tree --write-treeofc42afa30with origin/main5b41d44f(fetched with-c gc.auto=0 -c maintenance.auto=false) is clean, tree59de4c261c228274a5cff18aff953211de7002e3.setup-project.pyor the new test file. Every open PR's head was still merged in, and all ten are clean: adopt-project.py:plan --project NUL(andCon,com1,Lpt9) is refused asadopt-bad-project-namebefore anything exists, andcheckandexecutefind it in the plan'slegs.assembly, by the one question the scaffold andfamily.py initask (#1014) #10414a23c6e2, family.py and tests:Family.rewritten's refusal of twomembers:lines is asked of the method again, its docstring says no verb reaches that count since #974, and a refusal that leaves a ref behind failsassert_unchanged(#1029) #10395f924500, adopt-project.py, shape_materialize.py and standard.py: the Free-plan hint no longer calls an internal root private, adopt's docstring says its note for an internal root with an unread plan changed in #969, andprepare's docstring has no 104-character line (#994) #1038fd1de5ab, adopt-project.py:check's throwaway repositories are made in the work directory with the source's object format and no hook of this machine's; a path git refuses isplan-path-git-refuses, a repository git cannot make thereadopt-git-unasked(#910) #1031eea70be1, adopt-project.py: the split commit's message andplan's report name each path, question and rule id one way only and never raw, the history scan'sgit-failedis one line, and the table pads a name as stdout prints it (#900) #10309b4845bb, validate-family.py, validate-pins.py and both bootstrap.py copies: agitWindows cannot execute ([WinError 193], a namelessOSError) isgit-not-runnable, exit 2, with the put-git-on-PATH remedy, where each ended in a traceback, exit 1, and one test holds the four copies to one answer and one text (#1001) #10280c9eac6c, family.py:bump's fetch leaves out every refspec of the mount's whose source can name a tag, a pattern such asrefs/*and a short name too, so a mirror in a folder,+refs/*:refs/remotes/origin/all/*, no longer fails the fetch on a branch and a tag of one name or pins a commit only a tag reaches (#1009) #1027052f1e24, validate-family.py: a hand mount'sgit rmline works typed:git rm --cachedwhere a plain one refuses a staged gitlink, dies on a clone no section names or refuses what is no checkout, with what it leaves said, and a.gitmodulesgone from the work tree put back before thegit rmthat takes a section out (#936) #1025f83ca618, the family root's bootstrap.py and siblings.py: under GNU make 4.4's named-pipe jobserver, a member's make that says it is 4.4 or later is handed the holder'sMAKEFLAGSas it is, so it runs its jobs together again; every other make still gets no jobserver and no-j(#987) #10234d0aa1cf, the family root's bootstrap.py: a gitlink a member's own index gives no url, or a member's commit that a fetch reaching its remote did not bring, fails the run by name with its cure and no credential advice, where it saidfamily bootstrap okbeside git's exit 128 (#892) #954dc7fd7ea.Decided
setup-project.py's developer path, which never reads$OPENREPOSHAPE_REPO.self_bootstrapandstandard.pyare the only shipped Python that read it. Beside a tree with no.git, this check refuses it.standard.prepare, which--doctorand 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.pyis also open in adopt-project.py, shape_materialize.py and standard.py: the Free-plan hint no longer calls an internal root private, adopt's docstring says its note for an internal root with an unread plan changed in #969, andprepare's docstring has no 104-character line (#994) #1038.CONTROL_REandPATH_REhold\x00-\x1f\x7fonly, so a C1 control passeschecked_value. The launcher and the package refuse C1 (U+0080 to U+009F) in a repository. Withacme/r[U+009B]31mforged, the clone line prints the rawC2 9B, before and after this change.--intoand--local-remote-dirgo through the samePATH_RE(read in the code, not measured). That would be a follow-up forchecked_value, which every flag shares.🤖 Generated with Claude Code
Summary by Sourcery
Harden setup-project self-bootstrap by refusing visually misleading repository values before output or cloning.
New Features:
Bug Fixes:
git clone.Enhancements:
Tests: