Repository navigation
repo_shape.py and adopt-project.py: a local source's origin past GitHub's limits (an owner over 39 characters, a repository over 100) names no org/repo, by the rule _github_name reads a typed name by, so plan --org testorg is no longer told to pass --org <40 c's>, check holds the plan alike with origin there or gone, and the doctor's adoption line asks for --org (#1211) - #1304
brettheap wants to merge 14 commits into
Conversation
…efused as a path is, naming no GitHub repository and offering no url, so `--source <300 a's>/Thing` is no longer offered `https://github.com/<300 a's>/Thing`, which can never clone (#1003) `plan --source <300 a's>/Thing` is refused `source-unreadable`, "cannot be read (File name too long)" (#970). It also said "whether it names the path `./<300 a's>/Thing` here, or only the GitHub repository `<300 a's>/Thing`, cannot be told", and offered `https://github.com/<300 a's>/Thing`. Neither can be meant: a part longer than a file name may be is longer than GitHub lets an owner (39) or a repository (100) be named, so that url can never clone. `_source_there` now refuses an `ENAMETOOLONG` as it refuses a path: "so whether a repository is there cannot be told", with the cure for a cause other than permission, and no url. The same holds for the `github.com/` and `www.github.com/` spellings, and for a long repository part beside a `./<org>` directory. Any other refused `stat` of a name (a permission, an I/O error) still names both readings and offers the url. Part (a) of #1003, a source or recorded clone directory this user cannot enter, already landed on main in #1072 (see #918). It is measured again here and not changed. Closes #1003 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… offered its url only where GitHub can hold the name, so `testorg/<300 a's>` beside an unsearchable `./testorg` is offered no url, and `testorg/Thing` through a link too long keeps `https://github.com/testorg/Thing` (#1003) RV1122's SHOULD-1. #1122's first head refused a name as a path, with no url, where its `stat` was `File name too long`. It asked the errno, not the name, and that went wrong both ways: - `testorg/<300 a's>` beside a `./testorg` at mode 000 stops at `Permission denied` before the long part, and was still offered `https://github.com/testorg/<300 a's>`, which can never clone. - `testorg/Thing`, with `./testorg` a symlink to a name too long, is `File name too long` because of the link's destination. It lost the url of a repository GitHub can hold, which main offered. `_source_there` now decides by GitHub's limits, `GITHUB_OWNER_MAX` (39) and `GITHUB_NAME_MAX` (100), which the file already imports. An owner or a repository over them is refused as a path is, whatever reason the operating system gave, with the cure for that reason and no url. Any other name keeps both readings and its url. A part too long for the disk is over both limits, so `<300 a's>/Thing` keeps the first head's answer. The four controls now refuse a name as long as GitHub allows (an owner of 39, a repository of 100) with an I/O error, and expect its url. New rows: a name one over each limit, or too long for the disk, refused with an I/O error or a permission, offers no url (10 rows, in-process); the symlink keeps its url; and `plan` beside a mode-000 `./testorg` offers none. An owner of 40 to 255 characters, or a repository of 101 to 255, with nothing on disk is still handed to `gh`; see #1129. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… repository over 100, so `plan --source <50 b's>/Thing` never asks `gh` for a name GitHub cannot hold, no remedy offers `--org <50 b's>`, and `check` and `execute` never clone such a record again (#1129) GitHub gives an owner at most GITHUB_OWNER_MAX (39) characters and a repository at most GITHUB_NAME_MAX (100). `_github_name` now gives no name past either, counted once the `.git` is dropped, so every reader of it answers alike: `_source_spec` reads such a spelling as a path (in place where it is one, `source-unresolvable` where it is not), `_repository_in_url` refuses a url of it, and `_recorded_repository` and `_held_repository` read a plan's record of it as naming no repository. Stacked on #1122 (fix/1003-adopt-unenterable-source): its guard on the two limits in `_source_there` is folded into this one question, and its three `github.com/` rows move to the new file, since such a url is now refused before any `stat`. New rows: tests/test_adopt_source_github_limits.py (48). Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both of #1122's files (adopt-project.py, tests/test_adopt_unsearchable_source.py) conflicted, as S carries #1122's text and this branch carries #1122's commits plus #1194's edits to it; the ours side kept, which is the previous head's bytes. The merge changes no file against that head. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tHub's limits (an owner over 39 characters, a repository over 100) names no `org/repo`, by the rule `_github_name` reads a typed name by, so `plan --org testorg` is no longer told to pass `--org <40 c's>`, `check` holds the plan alike with `origin` there or gone, and the doctor's adoption line asks for `--org` (#1211) `url_repository`, the one reading of an `origin` that `adopt-project.py` and `shape-doctor.py` share, asked neither limit, though `_github_name` asks both of a typed or recorded `owner/repo` since #1129. Both now ask `repo_shape.github_can_hold(owner, repo)`, #1129's guard made one function, handed the repository as written: one trailing `.git`, in any case, is dropped before it is counted. - `GITHUB_NAME_MAX` and `GITHUB_OWNER_MAX` are defined in `scripts/repo_shape.py` now, which cannot import `scripts/shape_materialize.py`; `shape_materialize.py` imports them, so every other importer reads them where it did. - `plan` records `repository: null` for such an `origin`, compares nothing with it, and with no `--org` refuses `adopt-no-org`; the doctor's `the way in` line ends `--org <your-org>` for it. - At the limits nothing changes. NIT-3 (`--source github.com/<40 c's>/x` with that directory here) keeps its refusal. Rows: 28 in tests/test_adopt_source_github_limits.py and 4 in tests/test_shape_doctor.py's ORIGIN_ORGS; 20 fail at #1194's head 906172b, and the 12 controls pass on both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideAPICentralize GitHub owner and repository length limits in
ImplementationMake
TestsCover over-limit and boundary origin names across planning, checking, execution, and doctor guidance, including the trailing-slash regression case.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-11-10T10:46:18Z) RV1211: READY AFTER FIXES at 6b03c51 This is an independent review of #1304 (for #1211). It covers the one commit in the range The fixes it needs are one test row (SHOULD-1) and one table row in the body (NIT-3). There is no MUST and the code needs no change. 1. The scope choice: the wider change is rightThe limits belong in
2. FindingsMUST: none. SHOULD-1 (tests only): no row checks that the trailing
NIT-1 (follow-up, not this PR): the words of
NIT-2 (optional follow-up): no NOTE when such an
NIT-3 (body only): the Merges table's
3. Before and after, measuredThe probe.
Spot checks.
My own rows.
The doctor.
The changed outputs are what #1211 asks for, and the controls at 39 and 100 are byte-identical. 4. Rows and mutantsRows.
The assertions are tight: exact Mutants. Each was applied by a count-1 substitution in my worktree, run against
5. GatesEach gate ran at this head on a clean tree, one pytest at a time, with
6. Merges
7. The body
8. Not measured
Paths:
Lane 3's response (lane openRepoShape-3): SHOULD-1 and NIT-3 are taken. A fix round, FX1211, adds the |
…://github.com/testorg/<101 r's>.git/`, a repository of 101 with a trailing slash and an owner at the limit, so a `written` read from the url before its `/` is stripped is no longer a mutant that survives (#1211) RV1211's mutant R4 changes `written = path.rpartition("/")[2]` to `written = url.rpartition("/")[2]` in `url_repository`. It survived all 32 rows: the only origin ending in `/` was `both-over`, whose owner is also past its limit, which hid the repository case. The new row `repository-101-slash` has an owner at the limit, so only the repository's own count can set it aside. The row runs through both tests that read `ORIGINS_PAST` (`test_an_origin_is_read_by_the_rule_a_name_is` and `test_an_origin_github_cannot_hold_offers_no_org`): - at this head: `2 passed, 76 deselected`; - with #1194's `adopt-project.py`, `scripts/repo_shape.py` and `scripts/shape_materialize.py` swapped in: `2 failed, 76 deselected`; - under R4: `2 failed, 76 deselected`. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-11-10T10:55:48Z) RV1211b: READY AFTER FIXES at df942ef This is a delta review of #1304's fix round 1, the one commit The code and the new row are right. The only fix left is one sentence in the body, which is wrong about the row's owner. Once it is replaced, no re-review is needed. 1. The diff
2. The row's two idsI ran
3. The gate
I did not re-run the guards at this head. The delta is two lines in one test file. FX1211 reports 4. The body (
|
|
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 5 days and 2 hours by commenting |
…mits Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…n origin that #1268 hands every gh repo clone (#1129) #1194's rows were written before main's #1268 (2202af1, for #1261), which made `Source.clone` hand `gh repo clone` `--origin origin` after `--quiet`, so that the clone names its remote `origin` whatever `clone.defaultRemoteName` says. The logging `gh` the rows use keeps the argv it was given, and `cloned_by_gh` still spelled the old one, so at the predicted squash of #1194 over main 106f92f 14 rows failed (6 of `test_plan_asks_gh_for_a_name_github_can_hold`, 4 of `test_a_recorded_name_github_can_hold_is_cloned_again`, 4 of `test_check_and_execute_read_a_source_name_as_plan_reads_it`), each at `-- --quiet` against `-- --quiet --origin origin`. `cloned_by_gh` now spells main's call, as #1268 changed the exact pin in `tests/test_adopt_source_clone_record.py`. No row's intent changes: each still checks that `gh` is asked once, or not at all, for the name it was given. `adopt-project.py` and every other file are as the merge left them. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Lane: openRepoShape-3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-3, 2026-11-10T17:50:45Z) Restacked after #1194 landed. #1194 landed at 17:34Z as
It stays READY and 27th in v1.2.1's order. |



Lane 3's W1211 for #1211, RV1194's NIT-1 and NIT-2 on #1194 (#1194 issuecomment-6365280794). RV1211 and RV1211b read READY (#1304 issuecomment-6765337344 and issuecomment-6765516741).
Restacked after #1194 landed
#1194 landed at 17:34Z as
20e692acd, with final headeb2d7360f. Lane 3 restacked this PR onto it with checked merges:_github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194's final head;main, taking ours foradopt-project.pyandtests/test_adopt_source_github_limits.py, the two files that conflicted;mainitself, which was not needed: it had not moved past the squash.The head is now
ec240265c704d09ddf71c5766d3cea8ac0b0ea8f, and this PR's own change is its diff overmain(20e692acd), in five files.66a17ffc9, the treegit merge-tree --write-tree --merge-base 906172b41 20e692acd df942efeepredicts by applying this PR's own range onto the squash. adopt-project.py:_github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194's final head and its squash have one tree,b6228aec0. The script checked the tree after each merge and pushed only on a match.git diff -U0of its five files, without the@@andindexlines) over the squash are byte-identical to its lines over adopt-project.py:_github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194's head906172b41: 287 lines, compared withcmp._github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194's fix round changed the rows' helpercloned_by_ghto expect-- --quiet --origin originafter adopt-project.py: each cloneexecutemakes names its remoteorigin, whateverclone.defaultRemoteNamesays, soexecuteno longer ends inREFUSED mounting the legs("No such remote 'origin'") after both legs are pushed (#1261) #1268. It reaches this PR'stests/test_adopt_source_github_limits.pyrows with the squash.tests/test_adopt_source_github_limits.pywithtests/test_github_limits_init_check_org.py,tests/test_github_name_limits.py,tests/test_scaffold_leg_path_collisions.py,tests/test_no_auto_maintenance.pyandtests/test_update_add_codes_documented.py, gave 406 passed, 2 skipped in 128.71s, exit 0. The skips are the 2GIT_CONFIG_GLOBALrows oftests/test_no_auto_maintenance.py. The adopt modules and the doctor pair run next, and their lines follow on this thread.What a person sees change
A local source whose
originnames an owner over 39 characters or a repository over 100 (GitHub's limits,GITHUB_OWNER_MAXandGITHUB_NAME_MAX) names noorg/reponow, as a path does. On #1194's headurl_repository, the one reading of anorigin, asked neither limit, though_github_nameasks both of a typed or recordedowner/repo:plan --org testorgon a clone whoseoriginishttps://github.com/<40 c's>/Thing.gitorhttps://gitlab.com/<40 c's>/Thing.git(RV1194's P25 and P27) was refusedadopt-not-the-sourceand told to runplanagain with--org <40 c's>, whichexecuterefuses asadopt-org-too-long. A repository of 101 was told--project <101 r's>. Now the plan is written (exit 0), recordingrepository: null, and nothing is offered.checkon RV1194-cont's P29 plan (originhttps://gitlab.com/<40 c's>/Thing.git, planned with--org <40 c's>, itsorg:edited totestorg) foundplan-not-the-sourcewithoriginthere (exit 1,--org <40 c's>offered) andplan okwith it removed (exit 0). Both sayplan oknow:planrecords no repository for thatorigin, and the live reading names none either.planwith no--orgon such a clone wrote the plan withorg: <40 c's>, whichexecuterefuses. It isadopt-no-orgnow ("pass --org "), as for a pathorigin.shape-doctor.py's adoption line for such a clone (the way in) carried no--org, since it read<40 c's>asplandid. It ends--org <your-org>now, with its WHY, "this repository'soriginnames none thatplancan read", as for a pathorigin(shape-doctor.py, NOT A SHAPE ROOT on a git repository: print both ways in, as AGENTS.md says, and add--orgto the adopt line when there is noorigin#545). The line andplanstill read one answer.Nothing changes at the limits: an owner of 39 and a repository of 100 are read as before, by
plan,checkand the doctor (see the table).The change, and the choices
One rule,
repo_shape.github_can_hold(owner, repo). #1194's guard in_github_name(len(owner) > GITHUB_OWNER_MAX or len(repo) > GITHUB_NAME_MAX) becomes a function both readers call, so there is no second copy:_github_namehands it the repository as written (segments[1]), and it drops one trailing.git, in any case, before it counts: the count adopt-project.py:_github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194 made, at the same place in_github_name, with the same answer for every spelling (adopt-project.py:_github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194's 48 rows pass unchanged, and M6 below pins the hand-over).url_repositoryhands it the last segment of the url's path as written,.gitand all, so anoriginand the sameowner/repotyped get one answer:https://github.com/testorg/<100 r's>.git.gitnames nothing, astestorg/<100 r's>.git.gitnames nothing to_github_namesince adopt-project.py:_github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194.The limits move to
repo_shape.py.GITHUB_NAME_MAX(100) andGITHUB_OWNER_MAX(39) were defined inscripts/shape_materialize.py, which imports fromrepo_shape.py;repo_shape.pycannot import back, and is copied alone into every assembly root and family holder. So the two numbers are defined inrepo_shape.pynow, besideTOPIC_PREFIX, andshape_materialize.pyimports them from there, as it importsTOPIC_PREFIX. Every other importer (adopt-project.py,scaffold-project.py,scripts/family.py,tests/test_github_name_limits.py) still reads them fromshape_materialize, unchanged. One definition, as before.url_repository, not_remote_repository(the issue's "To settle"). Measured on #1194's head:planwith no--orgon a clone whoseoriginis<40 c's>/Thingreadorg: <40 c's>, and the doctor's adoption line, which reads the sameurl_repository(origin_org, #545), carried no--org. The two agree there. With the limits inadopt-project.py's_remote_repositoryalone,planwould refuse that lineadopt-no-org, while the doctor still offered it without--org: #545's case, made again.tests/test_shape_doctor.py'stest_the_adoption_line_names_an_org_only_where_plan_reads_noneholds_remote_repositoryandorigin_orgto one answer for eachorigin; three of its four new rows (below) fail on #1194's head, where both readers name<40 c's>(oropensoft); with the limits in_remote_repositoryalone, the test's next assertion, oforigin_org, would fail them instead (read, not run). Inurl_repository, both readers move together. The doctor's other rows are byte-identical (see the table).repo_shape.pyis digest-pinned in every estate. v1.2.1 already changes it (#1126, #1266 and #1274 since the v1.2.0 tag), so this adds no re-pin of its own.No NOTE for such an
origin. A pathorigingets a NOTE ("names no repository on GitHub, so--organd--projectwere not compared with it"), and a url that ends in noorg/repo(https://example.com/Thing.git) gets none. Anoriginpast the limits is a url, and is treated as that url is: no NOTE. The issue asks only thatplanrecord no repository and offer no name.adopt-no-org's words are unchanged. It says theorigin"ends in noorg/repo", which is whaturl_repositoryanswers for a pair GitHub cannot hold; it does not say which limit. Changing it would change everyadopt-no-org(#1194 leftsource-unresolvable's words alone for the same reason).NIT-3, decided: the refusal stays as it is.
--source github.com/<40 c's>/Thing, a directory here, is refusedsource-unresolvable("names noowner/repoon github.com"), remedy "pass a local clone's path, ororg/repo". That is the rule for everygithub.com/spelling that names noowner/repo, from before #1194: P28,--source github.com/acme/Thing/extrawith./github.com/acme/Thing/extrahere, gets the same refusal. The remedy works (--source ./github.com/<40 c's>/Thingis read in place, exit 0, noghcall, on both heads). Offering./<spelling>would change_repository_in_url's refusal for every such spelling, not for the limits alone, and the issue marks it optional. P24 and P28 are byte-identical before and after (table).Measured before and after
The probe ran the same 50 commands on #1194's head
906172b4and on this head, each in the lane's gate environment:ghfirst on PATH that refuses every call (exit 97),GH_TOKEN=bogus, an emptyGH_CONFIG_DIR;GIT_SSH_COMMAND=false,GIT_CONFIG_GLOBALan empty scratch file,GIT_CONFIG_NOSYSTEM=1,LANES_LANEandGIT_ALLOW_PROTOCOLunset;checkandexecutewith--local-remote-dir, and noexecutewith--yes;No
adopt-project.pycommand made aghcall on either head; the doctor made its own two,gh --versionandgh auth status(its machine row), on both. 21 commands changed and 29 are byte-identical. Every plan isplan --source <a clone with that origin> --project Thing, with the--orgshown.Changed (21):
originhttps://github.com/<40 c's>/Thing.gitplan --org testorgadopt-not-the-source, "Runplanagain with--project Thing --org <40 c's>"repository: null, nothing offeredhttps://gitlab.com/<40 c's>/Thing.gitrepository: nullhttps://github.com/<50 b's>/Thing.git--org <50 b's>repository: nullgit@github.com:<40 c's>/Thing.gitrepository: nullssh://git@github.com:22/<40 c's>/Thing.gitrepository: nullhttps://github.com/testorg/<101 r's>.gitadopt-not-the-source, "--project <101 r's> --org testorg"repository: nullhttps://github.com/<40 c's>/<101 r's>.gitadopt-not-the-source, both offeredrepository: nullhttps://github.com/testorg/<101 r's>.GITadopt-not-the-source(the source's own name is no remedy: a dot)repository: nullhttps://github.com/testorg/<100 r's>.git.gitadopt-not-the-source, the source read astestorg/<100 r's>.git, 104 charactersrepository: nullhttps://github.com/<40 c's>/Thing.gitplan, no--orgorg: <40 c's>,repository: <40 c's>/Thingadopt-no-org, "pass --org ", no planplan --org <40 c's>repository: <40 c's>/Thing, the source line says(origin: <40 c's>/Thing)repository: null, no(origin: ...)https://github.com/testorg/<101 r's>.gitplan --project <101 r's> --org testorgrepository: testorg/<101 r's>repository: nullhttps://gitlab.com/<40 c's>/Thing.gitplan --org <40 c's>repository: <40 c's>/Thingrepository: nullorg:edited totestorgcheck,originthereplan-not-the-source,--org <40 c's>offeredplan okexecute,originthereplan-not-the-sourceadopt-unconfirmed<40 c's>(github.com and gitlab.com),<50 b's>,testorg/<101 r's>, both over,testorg/<101 r's>.GIT(6 rows)shape-doctor.py --root <clone>the way in:plan --source <clone> --project <name>, no WHY--org <your-org>, and WHY "replace with the GitHub organisation to adopt under: this repository'soriginnames none thatplancan read"; every other row byte-identicalByte-identical (29):
<39 c's>/Thing(--org testorg:adopt-not-the-source,--org <39 c's>offered; no--org:org: <39 c's>);testorg/<100 r's>.gitand.../<100 r's>.git/(--project <100 r's>offered);testorg/<100 r's>.GITandtestorg/<97 r's>.GIT(read with the.GIT, as before);testorg/Thing(exit 0); a pathorigin(exit 0, the NOTE);https://example.com/Thing.git, no--org(adopt-no-org); noorigin(exit 0);checkandexecutewithoriginremoved (plan ok,adopt-unconfirmed); RV1194's C05 and C06 (a record of owner 40 or 50, noorigin:plan ok,adopt-unconfirmed) and their control at 39 (plan-not-the-source,--org <39 c's>), with the threeplans that wrote them: 11 commands;--source github.com/<40 c's>/Thingwith that directory here, and P28,--source github.com/acme/Thing/extrawith that directory here:source-unresolvable, "names noowner/repoon github.com", on both;<39 c's>/Thing,testorg/<100 r's>.git,testorg/<100 r's>.GIT,testorg/Thing, a path and a url with no pair (6 rows).Rows
34 new rows, in the two modules that already hold this area's rows: 22 fail at #1194's head
906172b4, at the assertion that names the defect, and all 34 pass here. The 12 controls pass on both. Fix round 1 added one row,repository-101-slash, which two tests read: it is 2 of the 34 and 2 of the 22.tests/test_adopt_source_github_limits.py, 30 rows. #1194's module for these two limits, "for every reader of the name": it holds the typed, url and recorded readers' rows, RV1194's C05 and C06 (test_a_record_github_cannot_hold_offers_no_org), and the logginggh,plannedandunconfirmedhelpers these rows reuse. Theoriginis one more reader of the same name.test_an_origin_is_read_by_the_rule_a_name_isurl_repository(<origin>)is None past a limit and the name at it, and_github_nameof the sameowner/repoas written agrees: owner 40 (github.com, gitlab.com), scp owner 50, repository 101,<100 r's>.git.git, both over, repository 101 with a trailing/(repository-101-slash); controls owner 39 (twice), repository 100,<100 r's>.GITurl_repository('<40 c's>/Thing' == None); 4 controls passtest_an_origin_github_cannot_hold_offers_no_orgplan --org testorg: exit 0, noadopt-not-the-source, no "Runplanagain", no(origin:,repository: None, noghcalladopt-not-the-source,--org <40 c's>)test_an_origin_github_can_hold_is_still_the_source(control)adopt-not-the-sourcenaming the source, as beforetest_plan_reads_no_org_from_an_origin_github_cannot_hold--org:adopt-no-org, no plan, for owner 40, gitlab owner 50 and repository 101; control owner 39 writesorg: <39 c's>org: <40 c's>, oradopt-not-the-sourcefor the repository); control passestest_a_plan_is_held_alike_with_origin_there_or_gonecheckandexecute(no--yes),originthere and removed: owner 40 isplan ok/adopt-unconfirmedboth times, no--org <40 c's>; control owner 39 isplan-not-the-sourcewith--org <39 c's>both timesplan-not-the-sourcewithoriginthere; 2 controls passThe last test needs git-filter-repo, as #1194's C05/C06 test does, and ran with 2.47.0 on PATH.
tests/test_shape_doctor.py, 4 rows inORIGIN_ORGS, the table oftest_the_adoption_line_names_an_org_only_where_plan_reads_none(#545): it holdsadopt-project.py's_remote_repositoryand the doctor'sorigin_orgto one answer for eachorigin, and the adoption line to--org <your-org>exactly where that answer is none. New:https://github.com/<40 c's>/Atlas.git,https://gitlab.com/<40 c's>/Atlas.git,git@github.com:opensoft/<101 r's>.git(none), and the controlhttps://github.com/<39 c's>/Atlas.git(<39 c's>). The three fail at #1194's head at the test's first assertion:_remote_repositoryread<40 c's>andopensoft(asorigin_orgdid); the control passes on both.Mutants
Each applied by exact substitution (count 1) to this change's code as it stood before the docstring revert under "Merges" (the only difference from this head is that docstring), run against
tests/test_adopt_source_github_limits.py,tests/test_adopt_origin_repository.pyand the doctor'sORIGIN_ORGStest (119 tests), then the file restored from its saved bytes and checked withcmp; after all of them both files' sha256 matched the ones taken before (repo_shape.py1c0b3c89..., which is this head's, andadopt-project.py26cfe258...). Unmutated: 119 passed. All 7 killed, at6b03c51a; fix round 1's mutant R4 is under "Fix round 1".url_repositorywithoutgithub_can_hold(#1194's head)url_repositoryhands over the name after its own.gitdrop, not as writtentest_an_origin_is_read_by_the_rule_a_name_is[repository-100-dot-git-dot-git],test_an_origin_github_cannot_hold_offers_no_org[repository-100-dot-git-dot-git]github_can_holddrops no.gittest_plan_asks_gh_for_a_name_github_can_hold[repository-100-dot-git], and therepository-100andrepository-100-dot-GITcontrols of two new tests<)<)_github_namehands over its.git-droppedrepo, notsegments[1]test_an_origin_is_read_by_the_rule_a_name_is[repository-100-dot-git-dot-git]+ 1)Gates
At
6b03c51a(the head before fix round 1; "Fix round 1" reran the two gates that hold the file it changes), a clean tree, one pytest at a time:/usr/bin/python3 -m pytest -q -p no:cacheprovider -rfs, Python 3.12.3, a basetemp per run, with a stubgh(exit 97) first on PATH, git-filter-repo 2.47.0 next,GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=false,GIT_CONFIG_GLOBALan empty scratch file,GIT_CONFIG_NOSYSTEM=1,EMAILand the test author set, andLANES_LANEandGIT_ALLOW_PROTOCOLunset. Never the full suite. Every run ended rc=0.test_adopt_source_github_limits,test_adopt_unsearchable_sourceadopt-not-the-source,plan-not-the-source,adopt-no-org), 1 of 2test_adopt_origin_repository,test_adopt_project_is_the_source,test_adopt_plan,test_adopt_check_plan_name,test_adopt_relative_submodule_url,test_adopt_values_as_typed,test_adopt_plan_flag_refusals_first,test_adopt_root_symlinks(its fixtures),test_adopt_git_reads,test_adopt_template_valuestest_adopt_source_clone_record,test_adopt_temp_work_roots(its fixtures),test_adopt_plan_and_remote_paths_spelled,test_adopt_plan_value_order,test_adopt_root_symlinks(their fixtures),test_adopt_split_branch,test_adopt_work_dir_refusaltest_github_name_limits,test_github_limits_init_check_org,test_scaffold_leg_path_collisions(one run: the second imports fixtures from the other two)test_repo_hygiene,test_cli_reference,test_agents_md_codes,test_git_output_decoding,test_stdout_encoding,test_path_guard_hygiene,test_no_auto_maintenance,test_update_add_codes_documentedurl_repositorythrough ittest_shape_doctor(all 453),test_doctor_unsearchable_links(imports its fixtures),test_shape_advisoryTotals: 2676 passed, 13 skipped, 0 failed. The skips are the host's:
SHAPE_MEDXEHR_CLONEunset (1); the reftable backend needs git 2.45 (3 in eachtest_adopt_root_symlinksrun); Windows' own default encoding (1);test_no_auto_maintenance's two rows that skip when the session began withGIT_CONFIG_GLOBALset (2); and the doctor's PowerShell line, Windows only (3).The modules were found by
git grep -lof the three codes and ofurl_repository,_remote_repository,origin_org,GITHUB_OWNER_MAXandGITHUB_NAME_MAXintests/, each with the modules it imports fixtures from.test_adopt_check_throwaway_reposmatched only a test's name (..._in_a_url_repository_...) and was not run.Merges
git merge-tree --write-tree, this headdf942efeagainstmainand each open PR that touches a file this PR changes (the list as RV1211 re-read it withgh pr list --state open --json number,headRefOid,files;main, #1194, #1280, #1294, #1265 and #1297 fetched by URL at fix round 1, and every head read again withgh pr listat 10:51Z, unmoved), beside the same merge of #1194's head906172b4alone:mainb607223b(it moved from5f06c3ebafter the gate, by #1282, which changesvalidate-manifest.py,validate-family.pyand three test files, none of mine; before that from34eb466e, by one commit that changes three test files)a8ce1a7240b6687cmain, as the gate began34eb466e6b7cb34b556898cbce1dd661adopt-project.py,scripts/repo_shape.py609e5b62608d7ae6d38e4e36adopt-project.py4c01904548532154791106c2adopt-project.pya840bd6aadd80e8fcc230f2fadopt-project.pye39f6c01fb0ed80ffa6d189fadopt-project.pye7e9990473d4893c3146c3ca(basefix/1114-pin-row-value-read-as)adopt-project.py,scripts/repo_shape.pybd9cfcbf15909b9f39b21c07adopt-project.py9ea9af0839f1b7035cb1abdbadopt-project.pya2232c1d2b64970f86ab76dbscripts/repo_shape.py5772c8cc5ef31e1fce9fbee6adopt-project.pyb0f422c1225670df2e020b45adopt-project.pyc01e6cb0b5e1bff0No conflict, mine or #1194's. No open PR touches
scripts/shape_materialize.py,tests/test_shape_doctor.pyor, besides #1194,tests/test_adopt_source_github_limits.py. A first, unpushed commit of this change also added a clause to_remote_repository's docstring; that conflicted with #1280's edit of the same docstring lines, so this head leaves that docstring as #1194 has it (url_repository's docstring says the rule).Fix round 1
RV1211 (issuecomment-6765337344) found no MUST and one SHOULD: no row checked that the trailing
/is stripped beforewrittenis read. Its mutant R4,written = url.rpartition("/")[2]in place ofwritten = path.rpartition("/")[2]inurl_repository(scripts/repo_shape.py), survived all 32 rows: the onlyoriginending in/wasboth-over, whose owner is also past its limit, which hid the repository case. Under R4,url_repository("https://github.com/testorg/<101 r's>.git/")readstestorg/<101 r's>; this head reads None.The row.
repository-101-slashinORIGINS_PASToftests/test_adopt_source_github_limits.py, right afterboth-over:f"{URL}{ORG}/{NAME_OVER}.git/", ownertestorg(7 characters, within its limit, so only the repository is past one), repository 101. Two tests readORIGINS_PAST(test_an_origin_is_read_by_the_rule_a_name_isandtest_an_origin_github_cannot_hold_offers_no_org), so it is 2 test ids. The commit isdf942efe, 2 lines in 1 file (git diff --name-only 6b03c51a df942efenames that file alone), so the code, the probe, M1 to M7 and the other gates above hold at this head.Its three runs (
-k repository-101-slash, the lane's gate environment; each source file restored from saved bytes and checked withcmp, all clean):2 passed, 76 deselected in 0.66s;_github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194'sadopt-project.py,scripts/repo_shape.pyandscripts/shape_materialize.pyswapped in:2 failed, 76 deselected in 0.87s(url_repositoryreadstestorg/<101 r's>, andplan --org testorgis refusedadopt-not-the-source);2 failed, 76 deselected in 0.97s(the same two assertions), so R4 is killed.Gates at this head, one pytest at a time,
-rs, the environment under "Gates", rc=0 each. Only one test file changes, so no other gate was run:test_adopt_source_github_limits,test_adopt_unsearchable_source(one run)test_repo_hygiene,test_cli_reference,test_agents_md_codes,test_git_output_decoding,test_stdout_encoding,test_path_guard_hygiene,test_no_auto_maintenance,test_update_add_codes_documentedThe skips are the same three as before: Windows' own default encoding (1) and the session's
GIT_CONFIG_GLOBAL(2).Out of scope
adopt-no-org's words, and--org's help (docs/cli.md): neither names this case. The help already lists only a path and nooriginamong the ways a source names no organisation; the refusal says theorigin"ends in noorg/repo", which isurl_repository's answer here.url_repositorydrops only a lowercase.git(adopt-project.py: read<org>/<repo>from everyorigingit accepts (a local path, a Windows backslash path, a trailing/or.git/) #521), sohttps://github.com/testorg/Thing.GITis read astestorg/Thing.GIT, as before. The limit counts that name with the.GITdropped, as_github_namecounts it, so no.GITname at the limits changes (the<100 r's>.GITand<97 r's>.GITrows above)._github_namereads no owner over 39 characters or repository over 100, soplan --source <50 b's>/Thingnever asksghfor a name GitHub cannot hold, no remedy offers--org <50 b's>, andcheckandexecutenever clone such a record again (#1129) #1194 left them:github_can_holdasks lengths only.adopt-no-organd of--org's help. For anoriginGitHub cannot hold,adopt-no-orgsays the url "ends in noorg/repo" though it visibly does, and--org's help (docs/cli.md) lists only a pathoriginor none, so a follow-up could add "or names one GitHub cannot hold" to both.originis set aside. A pathorigingets the NOTE that--organd--projectwere not compared with it, andplan --org testorgon anoriginpast the limits is now written silently, as for a url with no pair, so a person is not told why thatoriginwas ignored.Closes #1211
Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code
Summary by Sourcery
Make GitHub name-limit handling consistent across adoption planning, validation, and doctor guidance.
Bug Fixes:
Enhancements:
Tests: