Skip to content

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

Open
brettheap wants to merge 14 commits into
mainfrom
l3-1211-local-origin-github-limits

Conversation

@brettheap

@brettheap brettheap commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

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 head eb2d7360f. Lane 3 restacked this PR onto it with checked merges:

  1. adopt-project.py: _github_name reads no owner over 39 characters or 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) #1194's final head;
  2. its squash, now main, taking ours for adopt-project.py and tests/test_adopt_source_github_limits.py, the two files that conflicted;
  3. main itself, 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 over main (20e692acd), in five files.

What a person sees change

A local source whose origin names an owner over 39 characters or a repository over 100 (GitHub's limits, GITHUB_OWNER_MAX and GITHUB_NAME_MAX) names no org/repo now, as a path does. On #1194's head url_repository, the one reading of an origin, asked neither limit, though _github_name asks both of a typed or recorded owner/repo:

  • plan --org testorg on a clone whose origin is https://github.com/<40 c's>/Thing.git or https://gitlab.com/<40 c's>/Thing.git (RV1194's P25 and P27) was refused adopt-not-the-source and told to run plan again with --org <40 c's>, which execute refuses as adopt-org-too-long. A repository of 101 was told --project <101 r's>. Now the plan is written (exit 0), recording repository: null, and nothing is offered.
  • check on RV1194-cont's P29 plan (origin https://gitlab.com/<40 c's>/Thing.git, planned with --org <40 c's>, its org: edited to testorg) found plan-not-the-source with origin there (exit 1, --org <40 c's> offered) and plan ok with it removed (exit 0). Both say plan ok now: plan records no repository for that origin, and the live reading names none either.
  • plan with no --org on such a clone wrote the plan with org: <40 c's>, which execute refuses. It is adopt-no-org now ("pass --org "), as for a path origin.
  • shape-doctor.py's adoption line for such a clone (the way in) carried no --org, since it read <40 c's> as plan did. It ends --org <your-org> now, with its WHY, "this repository's origin names none that plan can read", as for a path origin (shape-doctor.py, NOT A SHAPE ROOT on a git repository: print both ways in, as AGENTS.md says, and add --org to the adopt line when there is no origin #545). The line and plan still read one answer.

Nothing changes at the limits: an owner of 39 and a repository of 100 are read as before, by plan, check and 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:

The limits move to repo_shape.py. GITHUB_NAME_MAX (100) and GITHUB_OWNER_MAX (39) were defined in scripts/shape_materialize.py, which imports from repo_shape.py; repo_shape.py cannot import back, and is copied alone into every assembly root and family holder. So the two numbers are defined in repo_shape.py now, beside TOPIC_PREFIX, and shape_materialize.py imports them from there, as it imports TOPIC_PREFIX. Every other importer (adopt-project.py, scaffold-project.py, scripts/family.py, tests/test_github_name_limits.py) still reads them from shape_materialize, unchanged. One definition, as before.

url_repository, not _remote_repository (the issue's "To settle"). Measured on #1194's head: plan with no --org on a clone whose origin is <40 c's>/Thing read org: <40 c's>, and the doctor's adoption line, which reads the same url_repository (origin_org, #545), carried no --org. The two agree there. With the limits in adopt-project.py's _remote_repository alone, plan would refuse that line adopt-no-org, while the doctor still offered it without --org: #545's case, made again. tests/test_shape_doctor.py's test_the_adoption_line_names_an_org_only_where_plan_reads_none holds _remote_repository and origin_org to one answer for each origin; three of its four new rows (below) fail on #1194's head, where both readers name <40 c's> (or opensoft); with the limits in _remote_repository alone, the test's next assertion, of origin_org, would fail them instead (read, not run). In url_repository, both readers move together. The doctor's other rows are byte-identical (see the table).

repo_shape.py is 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 path origin gets a NOTE ("names no repository on GitHub, so --org and --project were not compared with it"), and a url that ends in no org/repo (https://example.com/Thing.git) gets none. An origin past the limits is a url, and is treated as that url is: no NOTE. The issue asks only that plan record no repository and offer no name.

adopt-no-org's words are unchanged. It says the origin "ends in no org/repo", which is what url_repository answers for a pair GitHub cannot hold; it does not say which limit. Changing it would change every adopt-no-org (#1194 left source-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 refused source-unresolvable ("names no owner/repo on github.com"), remedy "pass a local clone's path, or org/repo". That is the rule for every github.com/ spelling that names no owner/repo, from before #1194: P28, --source github.com/acme/Thing/extra with ./github.com/acme/Thing/extra here, gets the same refusal. The remedy works (--source ./github.com/<40 c's>/Thing is read in place, exit 0, no gh call, 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 906172b4 and on this head, each in the lane's gate environment:

  • a logging gh first on PATH that refuses every call (exit 97), GH_TOKEN=bogus, an empty GH_CONFIG_DIR;
  • GIT_SSH_COMMAND=false, GIT_CONFIG_GLOBAL an empty scratch file, GIT_CONFIG_NOSYSTEM=1, LANES_LANE and GIT_ALLOW_PROTOCOL unset;
  • git-filter-repo 2.47.0 on PATH; every check and execute with --local-remote-dir, and no execute with --yes;
  • each source a one-commit repository with fixed dates, so the commit ids match across the two runs; the output's scratch paths replaced by one placeholder.

No adopt-project.py command made a gh call on either head; the doctor made its own two, gh --version and gh auth status (its machine row), on both. 21 commands changed and 29 are byte-identical. Every plan is plan --source <a clone with that origin> --project Thing, with the --org shown.

Changed (21):

Row origin Command #1194's head This head
P25 https://github.com/<40 c's>/Thing.git plan --org testorg exit 2, adopt-not-the-source, "Run plan again with --project Thing --org <40 c's>" exit 0, plan written, repository: null, nothing offered
P27 https://gitlab.com/<40 c's>/Thing.git the same the same refusal exit 0, repository: null
https://github.com/<50 b's>/Thing.git the same the same refusal, --org <50 b's> exit 0, repository: null
git@github.com:<40 c's>/Thing.git the same the same refusal exit 0, repository: null
ssh://git@github.com:22/<40 c's>/Thing.git the same the same refusal exit 0, repository: null
https://github.com/testorg/<101 r's>.git the same adopt-not-the-source, "--project <101 r's> --org testorg" exit 0, repository: null
https://github.com/<40 c's>/<101 r's>.git the same adopt-not-the-source, both offered exit 0, repository: null
https://github.com/testorg/<101 r's>.GIT the same adopt-not-the-source (the source's own name is no remedy: a dot) exit 0, repository: null
https://github.com/testorg/<100 r's>.git.git the same adopt-not-the-source, the source read as testorg/<100 r's>.git, 104 characters exit 0, repository: null
https://github.com/<40 c's>/Thing.git plan, no --org exit 0, org: <40 c's>, repository: <40 c's>/Thing exit 2, adopt-no-org, "pass --org ", no plan
the same plan --org <40 c's> exit 0, repository: <40 c's>/Thing, the source line says (origin: <40 c's>/Thing) exit 0, repository: null, no (origin: ...)
https://github.com/testorg/<101 r's>.git plan --project <101 r's> --org testorg exit 0, repository: testorg/<101 r's> exit 0, repository: null
P29 https://gitlab.com/<40 c's>/Thing.git plan --org <40 c's> exit 0, repository: <40 c's>/Thing exit 0, repository: null
P29 the same, org: edited to testorg check, origin there exit 1, plan-not-the-source, --org <40 c's> offered exit 0, plan ok
P29 the same execute, origin there exit 2, plan-not-the-source exit 2, adopt-unconfirmed
doctor <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 the same line ending --org <your-org>, and WHY "replace with the GitHub organisation to adopt under: this repository's origin names none that plan can read"; every other row byte-identical

Byte-identical (29):

  • at the limits: <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>.git and .../<100 r's>.git/ (--project <100 r's> offered); testorg/<100 r's>.GIT and testorg/<97 r's>.GIT (read with the .GIT, as before);
  • testorg/Thing (exit 0); a path origin (exit 0, the NOTE); https://example.com/Thing.git, no --org (adopt-no-org); no origin (exit 0);
  • P29's check and execute with origin removed (plan ok, adopt-unconfirmed); RV1194's C05 and C06 (a record of owner 40 or 50, no origin: plan ok, adopt-unconfirmed) and their control at 39 (plan-not-the-source, --org <39 c's>), with the three plans that wrote them: 11 commands;
  • P24, --source github.com/<40 c's>/Thing with that directory here, and P28, --source github.com/acme/Thing/extra with that directory here: source-unresolvable, "names no owner/repo on github.com", on both;
  • the doctor on <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 logging gh, planned and unconfirmed helpers these rows reuse. The origin is one more reader of the same name.

Test Rows What it asserts #1194's head This head
test_an_origin_is_read_by_the_rule_a_name_is 11 url_repository(<origin>) is None past a limit and the name at it, and _github_name of the same owner/repo as 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>.GIT 7 fail at url_repository ('<40 c's>/Thing' == None); 4 controls pass 11 pass
test_an_origin_github_cannot_hold_offers_no_org 7 P25 and P27 and kin, plan --org testorg: exit 0, no adopt-not-the-source, no "Run plan again", no (origin: , repository: None, no gh call 7 fail at the exit (adopt-not-the-source, --org <40 c's>) 7 pass
test_an_origin_github_can_hold_is_still_the_source (control) 4 at the limits, adopt-not-the-source naming the source, as before 4 pass 4 pass
test_plan_reads_no_org_from_an_origin_github_cannot_hold 4 no --org: adopt-no-org, no plan, for owner 40, gitlab owner 50 and repository 101; control owner 39 writes org: <39 c's> 3 fail (exit 0 with org: <40 c's>, or adopt-not-the-source for the repository); control passes 4 pass
test_a_plan_is_held_alike_with_origin_there_or_gone 4 P29, check and execute (no --yes), origin there and removed: owner 40 is plan ok / adopt-unconfirmed both times, no --org <40 c's>; control owner 39 is plan-not-the-source with --org <39 c's> both times 2 fail at plan-not-the-source with origin there; 2 controls pass 4 pass

The 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 in ORIGIN_ORGS, the table of test_the_adoption_line_names_an_org_only_where_plan_reads_none (#545): it holds adopt-project.py's _remote_repository and the doctor's origin_org to one answer for each origin, 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 control https://github.com/<39 c's>/Atlas.git (<39 c's>). The three fail at #1194's head at the test's first assertion: _remote_repository read <40 c's> and opensoft (as origin_org did); 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.py and the doctor's ORIGIN_ORGS test (119 tests), then the file restored from its saved bytes and checked with cmp; after all of them both files' sha256 matched the ones taken before (repo_shape.py 1c0b3c89..., which is this head's, and adopt-project.py 26cfe258...). Unmutated: 119 passed. All 7 killed, at 6b03c51a; fix round 1's mutant R4 is under "Fix round 1".

Mutant Killed by
M1: url_repository without github_can_hold (#1194's head) 20: every new row that is not a control
M2: url_repository hands over the name after its own .git drop, not as written 2: test_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]
M3: github_can_hold drops no .git 5: #1194's test_plan_asks_gh_for_a_name_github_can_hold[repository-100-dot-git], and the repository-100 and repository-100-dot-GIT controls of two new tests
M4: owner limit one lower (<) 27
M5: repository limit one lower (<) 14
M6: _github_name hands over its .git-dropped repo, not segments[1] 1: test_an_origin_is_read_by_the_rule_a_name_is[repository-100-dot-git-dot-git]
M7: owner limit one higher (+ 1) 22

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 stub gh (exit 97) first on PATH, git-filter-repo 2.47.0 next, GH_TOKEN=bogus, an empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false, GIT_CONFIG_GLOBAL an empty scratch file, GIT_CONFIG_NOSYSTEM=1, EMAIL and the test author set, and LANES_LANE and GIT_ALLOW_PROTOCOL unset. Never the full suite. Every run ended rc=0.

Run Modules Result
rows, and #1194's changed modules test_adopt_source_github_limits, test_adopt_unsearchable_source 116 passed
the codes this changes the answer of (adopt-not-the-source, plan-not-the-source, adopt-no-org), 1 of 2 test_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_values 648 passed, 4 skipped
the same, 2 of 2 test_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_refusal 632 passed, 3 skipped
the limits, and the leg-path rows test_github_name_limits, test_github_limits_init_check_org, test_scaffold_leg_path_collisions (one run: the second imports fixtures from the other two) 264 passed
the guards 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_documented 470 passed, 3 skipped
the doctor, whole, and the modules that read url_repository through it test_shape_doctor (all 453), test_doctor_unsearchable_links (imports its fixtures), test_shape_advisory 546 passed, 3 skipped

Totals: 2676 passed, 13 skipped, 0 failed. The skips are the host's: SHAPE_MEDXEHR_CLONE unset (1); the reftable backend needs git 2.45 (3 in each test_adopt_root_symlinks run); Windows' own default encoding (1); test_no_auto_maintenance's two rows that skip when the session began with GIT_CONFIG_GLOBAL set (2); and the doctor's PowerShell line, Windows only (3).

The modules were found by git grep -l of the three codes and of url_repository, _remote_repository, origin_org, GITHUB_OWNER_MAX and GITHUB_NAME_MAX in tests/, each with the modules it imports fixtures from. test_adopt_check_throwaway_repos matched only a test's name (..._in_a_url_repository_...) and was not run.

Merges

git merge-tree --write-tree, this head df942efe against main and each open PR that touches a file this PR changes (the list as RV1211 re-read it with gh 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 with gh pr list at 10:51Z, unmoved), beside the same merge of #1194's head 906172b4 alone:

Against Head Files of mine it touches #1194's head This head
main b607223b (it moved from 5f06c3eb after the gate, by #1282, which changes validate-manifest.py, validate-family.py and three test files, none of mine; before that from 34eb466e, by one commit that changes three test files) clean, a8ce1a72 clean, 40b6687c
main, as the gate began 34eb466e clean, 6b7cb34b clean, 556898cb
#1171 ce1dd661 adopt-project.py, scripts/repo_shape.py clean, 609e5b62 clean, 608d7ae6
#1198 d38e4e36 adopt-project.py clean, 4c019045 clean, 48532154
#1210 791106c2 adopt-project.py clean, a840bd6a clean, add80e8f
#1225 cc230f2f adopt-project.py clean, e39f6c01 clean, fb0ed80f
#1237 fa6d189f adopt-project.py clean, e7e99904 clean, 73d4893c
#1249 3146c3ca (base fix/1114-pin-row-value-read-as) adopt-project.py, scripts/repo_shape.py clean, bd9cfcbf clean, 15909b9f
#1265 39b21c07 adopt-project.py clean, 9ea9af08 clean, 39f1b703
#1280 5cb1abdb adopt-project.py clean, a2232c1d clean, 2b64970f
#1284 86ab76db scripts/repo_shape.py clean, 5772c8cc clean, 5ef31e1f
#1294 ce9fbee6 adopt-project.py clean, b0f422c1 clean, 225670df
#1297 2e020b45 adopt-project.py clean, c01e6cb0 clean, b5e1bff0

No conflict, mine or #1194's. No open PR touches scripts/shape_materialize.py, tests/test_shape_doctor.py or, 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 before written is read. Its mutant R4, written = url.rpartition("/")[2] in place of written = path.rpartition("/")[2] in url_repository (scripts/repo_shape.py), survived all 32 rows: the only origin ending in / was both-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/") reads testorg/<101 r's>; this head reads None.

The row. repository-101-slash in ORIGINS_PAST of tests/test_adopt_source_github_limits.py, right after both-over: f"{URL}{ORG}/{NAME_OVER}.git/", owner testorg (7 characters, within its limit, so only the repository is past one), repository 101. Two tests read ORIGINS_PAST (test_an_origin_is_read_by_the_rule_a_name_is and test_an_origin_github_cannot_hold_offers_no_org), so it is 2 test ids. The commit is df942efe, 2 lines in 1 file (git diff --name-only 6b03c51a df942efe names 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 with cmp, all clean):

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:

Run Modules Result
rows test_adopt_source_github_limits, test_adopt_unsearchable_source (one run) 118 passed in 48.54s
the guards 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_documented 470 passed, 3 skipped in 71.18s (0:01:11)

The skips are the same three as before: Windows' own default encoding (1) and the session's GIT_CONFIG_GLOBAL (2).

Out of scope

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:

  • Treat local origins with GitHub-incompatible owner or repository lengths as naming no repository, preventing invalid adoption suggestions and keeping plan and check behavior consistent whether the origin is present or removed.

Enhancements:

  • Centralize GitHub owner and repository length validation in repo_shape.py and share it across typed, recorded, and origin repository-name parsing.
  • Update shape-doctor adoption guidance to request an organisation when an origin cannot identify a GitHub-compatible repository.

Tests:

  • Add coverage for over-limit owners and repositories, boundary values, URL spellings, trailing paths, planning without an organisation, and plan validation with or without an origin.

Brett Heap and others added 8 commits October 21, 2026 13:09
…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>
…, so this branch holds the commits #1122 squash-landed

Co-Authored-By: Claude Sonnet 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>
…into fix/1129-github-name-limits

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>
@sourcery-ai

sourcery-ai Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

API

Centralize GitHub owner and repository length limits in repo_shape.py and expose github_can_hold so origin and typed-name readers can apply the same rule, including the trailing .git handling.

  • scripts/repo_shape.py L78-84, L3020-3040, L3071-3082 (+2 small edits)
  • scripts/shape_materialize.py L89-91, L6349-6351, L6354 (removed lines)

Implementation

Make _github_name use the shared GitHub-limit rule on the repository as written, keeping typed-name readings consistent with origins.

  • adopt-project.py L395-400, L2181-2183, L2194-2195

Tests

Cover over-limit and boundary origin names across planning, checking, execution, and doctor guidance, including the trailing-slash regression case.

  • tests/test_adopt_source_github_limits.py L54, L485-672
  • tests/test_shape_doctor.py L9768-9778

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@brettheap

Copy link
Copy Markdown
Contributor Author

(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 906172b4..6b03c51a, which is stacked on #1194 at 906172b4. I wrote none of it. I checked the head first with gh pr view 1304 --json headRefOid: it is 6b03c51a and did not move while I worked.

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 right

The limits belong in url_repository, as the writer put them, and should not be cut down to _remote_repository with the doctor left to a follow-up. This is measured, not just reasoned.

2. Findings

MUST: none.

SHOULD-1 (tests only): no row checks that the trailing / is stripped before written is read.

NIT-1 (follow-up, not this PR): the words of adopt-no-org and --org's help.

  • For https://github.com/<40 c's>/Thing.git with no --org, a person now reads "its URL ... ends in no org/repo" (adopt-project.py:5733-5736). The url visibly does end in one; GitHub just cannot hold it.
  • --org's help (adopt-project.py:18847-18850, and docs/cli.md) still lists only a path origin or none as naming no organisation.
  • The remedy ("pass --org <org>") is right, and the body gives its reason for leaving the words alone. A follow-up issue could add "or names one GitHub cannot hold" to both.

NIT-2 (optional follow-up): no NOTE when such an origin is set aside.

  • For P25, plan --org testorg is now written silently: no (origin: ...) and no NOTE.
  • A path origin gets the NOTE "--org and --project were not compared with it". The PR follows the rule for a url with no pair (https://example.com/Thing.git, which gets no NOTE), so this is consistent, but a person is not told why their origin was ignored.

NIT-3 (body only): the Merges table's main row is stale.

3. Before and after, measured

The probe.

  • I copied the writer's probe.sh into my scratch and ran it from there; I ran nothing inside W1211 or W1211-scratch. It holds the writer's 50 commands, and I added 11 of my own (X1-X8, plus the doctor on X1-X3).
  • It ran at 906172b4 and at 6b03c51a with:
    • a logging gh stub (exit 97) first on PATH, then git-filter-repo 2.47.0;
    • GH_TOKEN=bogus and an empty GH_CONFIG_DIR;
    • GIT_SSH_COMMAND=false, an empty scratch GIT_CONFIG_GLOBAL and GIT_CONFIG_NOSYSTEM=1;
    • LANES_LANE and GIT_ALLOW_PROTOCOL unset;
    • --local-remote-dir on every check and execute, and no --yes.
  • Results:
    • The writer's 50 commands: 21 changed, 29 byte-identical, as the body says.
    • All 61 commands: 28 changed, 33 byte-identical.
    • Every adopt-project.py command made 0 gh calls on both heads. The doctor made 2 on both (--version, auth status).

Spot checks.

  • P25 and P27 (--org testorg):
    • before: exit 2, REFUSED adopt-not-the-source: ... the source is <40c>/Thing, remedy "Run plan again with --project Thing --org <40c>";
    • after: exit 0, plan written, repository: null, no (origin: .
  • P29 (plan --org <40c>, then org: edited to testorg):
    • the plan now records repository: null;
    • check with origin there: exit 1 plan-not-the-source before, exit 0 plan ok after;
    • execute with origin there: exit 2 plan-not-the-source before, exit 2 adopt-unconfirmed after;
    • with origin removed, both are byte-identical (plan ok, adopt-unconfirmed).
  • C05, C06 and C07 (plan, check, execute): byte-identical. C05 and C06 give plan ok and C07 gives plan-not-the-source.
  • N1 (no --org): before exit 0 with org: <40c>, after exit 2 REFUSED adopt-no-org.
  • N2, N3, P26d and K8 match the body's table exactly.
  • P24 and P28 are byte-identical (source-unresolvable).
  • The NIT-3 remedy works: --source ./github.com/<40c>/Thing gives exit 0, 0 gh calls and repository: null on both heads.

My own rows.

  • These changed the same way P25 did (before exit 2 adopt-not-the-source, after exit 0 and repository: null):
    • X1 https://github.com/<40c>/Thing.git/;
    • X2 git@gitlab.com:<40c>/Thing (no .git);
    • X4 https://github.com/testorg/<101r>.git/.
  • These control rows at the limits are byte-identical:
    • X3 <39c>/<100r>.git with --org testorg;
    • X6 the same with no --org (org: <39c>);
    • X5 <100r>.Git.
  • X7 and X8 (check and execute of the P25 plan written at this head) give plan ok and adopt-unconfirmed.

The doctor.

  • A diff of the whole output for each of the 8 changed doctor rows shows only one change: the way-in NEXT line gains --org <your-org>, and one WHY line is added. The doctor on X3 (at the limits) is byte-identical, with no --org, matching X6's org: <39c>.

url_repository on 13,440 urls.

  • 2,453 answers change, and every one changes to None. Each changed url is in one of these groups:
    • owner over 39: 1,056;
    • repository over 100 once one .git is dropped: 964;
    • both: 192;
    • <100r>.git.git: 241. That name counts as <100r>.git, 104 characters, which is how _github_name counts it.
  • No owner over 39 is still read (0).

The changed outputs are what #1211 asks for, and the controls at 39 and 100 are byte-identical.

4. Rows and mutants

Rows.

The assertions are tight: exact url_repository values, exact refusal prefixes, the exit codes, repository: None, and an empty gh log.

Mutants. Each was applied by a count-1 substitution in my worktree, run against tests/test_adopt_source_github_limits.py plus the doctor's ORIGIN_ORGS test, then restored and checked with cmp.

Mutant Result
R1: the narrow fix (#1194's url_repository, limits in _remote_repository) killed, 10 failed, 79 passed
R2: github_can_hold drops only a lowercase .git killed, 2 failed, 87 passed (the repository-100-dot-GIT controls of the read-rule and still-the-source tests)
R3: github_can_hold drops every trailing .git killed, 2 failed, 87 passed (the repository-100-dot-git-dot-git rows)
R4: written taken from the url before / is stripped survives, 89 passed. This is SHOULD-1, and the proposed row kills it.

5. Gates

Each gate ran at this head on a clean tree, one pytest at a time, with -rs and the environment above.

Modules Result
test_adopt_source_github_limits + test_adopt_unsearchable_source 116 passed in 43.74s (matches the body)
test_shape_doctor alone, in the background 450 passed, 3 skipped in 526.23s (0:08:46). Skips: the PowerShell line, Windows only.
guards: 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_documented 470 passed, 3 skipped in 74.72s (0:01:14). Skips: Windows' default encoding (1), and GIT_CONFIG_GLOBAL set at session start (2). Matches the body.
test_github_name_limits, test_github_limits_init_check_org, test_scaffold_leg_path_collisions 264 passed in 57.95s (matches the body)
pins and pinned copies: test_pinned_copy_text (with test_link_source_chain_defaults, which it imports), test_pin_validator, test_validate_pins_named_and_spelled, test_validate_pins_spelled_and_named, test_validate_pins_without_git, test_release_workflow, test_scaffold_pin_and_reuse, test_openreposhape_package 1014 passed in 139.96s (0:02:19)
the kept repo_shape.py: test_update_shape_kept_helper + test_update_shape (its fixture) 275 passed in 109.56s (0:01:49)

6. Merges

git merge-tree --write-tree of each head against the current main and the four PRs the brief names:

Against Head #1194's head This head
main b607223b clean, a8ce1a72 clean, 46822144
#1280 5cb1abdb clean, a2232c1d clean, cf6d1df1
#1294 ce9fbee6 clean, b0f422c1 clean, 9624c408
#1265 39b21c07 clean, 9ea9af08 clean, 0848a1bb
#1297 2e020b45 clean, c01e6cb0 clean, 5be2b3e7

7. The body

  • It has the Stacked section with the range to review: yes.

  • It says plainly that repo_shape.py, a pinned copy, changes, and why: yes, under "The limits move" and "url_repository, not _remote_repository", with the v1.2.1 re-pin reasoning. Every point of that reasoning matched my measurements.

  • Closes #1211 is the only closing keyword, and closingIssuesReferences reads [1211].

  • It is ASCII except the robot line (one non-ASCII character, U+1F916, on that line).

  • The bot's "Summary by Sourcery" block sits after the robot line. It is ASCII and adds no closing keyword.

  • Every number I re-measured matches:

    • 21 changed and 29 byte-identical;
    • 32 rows with 20 failing, and the per-test breakdown;
    • 48 old rows;
    • 116, 470/3, 264 and 453 (450 passed, 3 skipped) in the gates;
    • repo_shape.py sha256 1c0b3c89...;
    • the four PR merge trees.

    The only exception is the main row (NIT-3). If SHOULD-1 is taken, the Rows counts become 34 and 22.

8. Not measured

  • The writer's mutants M1-M7: I added R1-R4 instead.
  • The body's two "codes" gates (648 passed / 4 skipped and 632 passed / 3 skipped), and test_doctor_unsearchable_links and test_shape_advisory.
  • CI's tests job, which was still in progress at 10:42Z (package, tests-git-bash and SonarCloud had passed).

Paths:

  • clone: /workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV1211 (detached at 6b03c51a, clean)
  • scratch: /workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV1211-scratch
  • worktrees: RV1211-scratch/w1194 (at 906172b4) and RV1211-scratch/wmut (at 6b03c51a, clean, every file checked with cmp)
  • probe results: RV1211-scratch/probe/{before,after}/results.txt
  • logs: gate-*.log, mut-R*.log, rows-*.log, slash-*.log
  • basetemps: /tmp/claude-1000/l3bt/RV1211-*

Lane 3's response (lane openRepoShape-3): SHOULD-1 and NIT-3 are taken. A fix round, FX1211, adds the repository-101-slash row and refreshes the body's counts and its main row. An opus delta review follows. NIT-1 and NIT-2 are follow-up candidates: lane 3 files nothing new without Brett Heap's word, and names them to him. The scope choice stands as the writer made it. #1304 stays a draft with no milestone until it reads READY, and lands after #1194 by a restack onto its squash.

…://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>
@brettheap

Copy link
Copy Markdown
Contributor Author

(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 6b03c51a..df942efe. The PR head reads df942efe (checked first with gh pr view 1304 --json headRefOid, and again at the end), and closingIssuesReferences reads [1211]. I wrote none of this fix round.

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

  • One file, two lines. git diff --stat 6b03c51a df942efe shows tests/test_adopt_source_github_limits.py | 2 ++ and nothing else.
  • The row is exactly the one RV1211 gave. I applied RV1211's row to the 6b03c51a copy of the file with my own script, and the result is byte-identical to df942efe's copy (cmp clean). The row sits in ORIGINS_PAST, right after both-over:
        pytest.param(f"{URL}{ORG}/{NAME_OVER}.git/", ORG, NAME_OVER,
                     id="repository-101-slash"),
    
  • The commit itself:
    • its parent is 6b03c51a;
    • author and committer are Brett Heap <service@opensoft.dev>;
    • the trailer is Co-Authored-By: Claude Sonnet 5.5;
    • adopt-project.py, scripts/repo_shape.py and scripts/shape_materialize.py are byte-identical to 6b03c51a's, so the review of the code at 6b03c51a stands.

2. The row's two ids

I ran -k repository-101-slash in my own worktree, in the lane gate environment. Each swapped file was restored from saved bytes and checked with cmp, and all four were clean (porcelain 0).

Tree Result
at df942efe 2 passed, 76 deselected in 0.98s
#1194's three files swapped in 2 failed, 76 deselected in 0.90s. The failures are test_an_origin_is_read_by_the_rule_a_name_is[repository-101-slash] and test_an_origin_github_cannot_hold_offers_no_org[repository-101-slash].
under R4 (written = url.rpartition("/")[2]) 2 failed, 76 deselected in 0.93s, the same two ids. R4 is now killed.

3. The gate

tests/test_adopt_source_github_limits.py with tests/test_adopt_unsearchable_source.py, in one run, at df942efe on a clean tree, with -rs:

118 passed in 50.21s (rc=0). This matches FX1211's 118: the 116 at 6b03c51a plus the 2 new ids.

I did not re-run the guards at this head. The delta is two lines in one test file. FX1211 reports 470 passed, 3 skipped, and I measured the same result at 6b03c51a.

4. The body (pr-body-fx.md, sha256 186fb5450162cb46...)

Findings

MUST: none. SHOULD: none.

NIT-1 (body only, the one fix). The "Fix round 1" section says the row's owner is "at the limit". It is not: ORG is testorg (from tests/test_adopt_temp_work_roots.py:49), 7 characters, well inside the 39 limit. That is the whole point of the row: only the repository is past a limit. Replace this sentence:

The row. repository-101-slash in ORIGINS_PAST of tests/test_adopt_source_github_limits.py, right after both-over: f"{URL}{ORG}/{NAME_OVER}.git/", owner testorg (at the limit), repository 101.

with:

The row. repository-101-slash in ORIGINS_PAST of tests/test_adopt_source_github_limits.py, right after both-over: f"{URL}{ORG}/{NAME_OVER}.git/", owner testorg (7 characters, within its limit, so only the repository is past one), repository 101.

NIT-2 (no action on the branch). The pushed commit's subject says the same thing ("an owner at the limit"). It must not be amended. Lane 3 should just not copy that phrase into the squash message.

Paths

  • clone: /workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV1211 (now detached at df942efe, clean)
  • scratch: /workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV1211-scratch
    • worktree wmut, at df942efe, clean
    • logs: rowb-head.log, rowb-swap1194.log, rowb-R4.log, gate-limitsb.log
    • the body as reviewed: pr-body-fx.md
  • basetemps: /tmp/claude-1000/l3bt/RV1211-slash and /tmp/claude-1000/l3bt/RV1211-limits

Not re-measured at df942efe: the guards, the doctor, the pin and kept-copy modules, and the other merge rows. They were measured at 6b03c51a, and the delta does not touch the code they exercise.

Lane 3's response (lane openRepoShape-3): NIT-1 is taken word for word. The live body is now round 1 (sha256 a783b0ebf9115efd), with the Sourcery block as the bot left it, and closingIssuesReferences reads [1211]. NIT-2 is noted: the squash message will not say "an owner at the limit". As the review says, no re-review is needed. So #1304 is READY at df942efe. Lane 3 sets v1.2.1, marks it ready, and places it on #473 by its READY time. It lands after #1194, by a restack onto #1194's squash, and that restack gets its own delta review.

@brettheap brettheap added this to the v1.2.1 milestone Nov 10, 2026
@brettheap
brettheap marked this pull request as ready for review November 10, 2026 10:56
@sourcery-ai

sourcery-ai Bot commented Nov 10, 2026

Copy link
Copy Markdown

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 @sourcery-ai review. Upgrade to get a review now.

Brett Heap and others added 2 commits November 10, 2026 13:47
…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>
brettheap and others added 3 commits November 10, 2026 13:32
Lane: openRepoShape-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… that conflicted

The PR's own no-context lines over the squash equal its lines over #1194's head 906172b, and the tree is 66a17ff, the tree of this PR's own range applied to #1194's squash.

Lane: openRepoShape-3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@brettheap

Copy link
Copy Markdown
Contributor Author

(lane openRepoShape-3, 2026-11-10T17:50:45Z) Restacked after #1194 landed. #1194 landed at 17:34Z as 20e692acd, with final head eb2d7360f. Lane 3's checked script (restack-1304.sh) merged that final head, then the squash, now main. It took ours for adopt-project.py and tests/test_adopt_source_github_limits.py, the two files that conflicted. main had not moved past the squash.

It stays READY and 27th in v1.2.1's order.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant