Repository navigation
scaffold-project.py, family.py init and shape_materialize.py: the plan names the tracking branch, and on disk the visibility; an unread root or reused leg is said to be unread, the root before the plan; every holder gets the members' NOTE (#913) - #969
Conversation
…lan names the tracking branch, and on disk the visibility, and says "not found" is what this token can see; a root or reused leg whose visibility GitHub did not give is said to be unread, the root before the plan and read once; every holder gets the members' credential NOTE; and an internal root's unread plan is no longer tied to the Free plan (#913) - U163, L2-92: `_reused_leg_visibility` returns None for a read that failed, where it returned the run's visibility, and `_private_leg_note` names each such leg in a NOTE of its own, goes on by the legs it could read, and asks the Free-plan note when a leg is closed or unread. - U167, L2-96: `_leg_visibility` reads the root once, through `repository_visibility_answer`, which tells gh's not-found answer apart, and says before the plan when the read failed, so `--visibility` can be passed before any leg exists; `_create_remotes` takes that read instead of asking GitHub again. - U164, L2-93: an internal root whose organisation's plan is unread is told `FREE_PLAN_UNREAD_INTERNAL`, which says the plan is not what the hint hangs on. - U166, L2-95: `init` prints the credential NOTE for every holder on GitHub, worded about the private or internal members a person adds. - U168, L2-97: the `not-found-on-stdout-alone` row holds gh's not-found words on stdout. - U010, AT-scaffold N3: the scaffold's and `init`'s plans have a `branch` row, and under `--local-remote-dir` a `visibility` row. - U060, L2-61: the `remotes` row says it creates what gh does not find with this token, and that a private repository it cannot see is not found either. U039 ("Needs a ruling": what `visibility:` records) is left as it is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR refines GitHub visibility handling across scaffold and family initialization, explicitly distinguishing unread repositories from not-found ones, reusing a single root read, improving plan and credential diagnostics, and adding comprehensive regression tests for the resulting behavior. Sequence diagram for scaffold visibility planning and remote creationsequenceDiagram
participant Scaffold as scaffold-project.py
participant Visibility as shape_materialize.repository_visibility_answer
participant GitHub as GitHub gh CLI
participant Plan as Plan output
participant Remotes as Remote creation
Scaffold->>Visibility: repository_visibility_answer(root)
Visibility->>GitHub: gh repo view root --json visibility
GitHub-->>Visibility: visibility, REPOSITORY_NOT_FOUND, or failure
Visibility-->>Scaffold: root_seen
alt root read failed
Scaffold->>Plan: Print unread-root NOTE before plan
else root not found
Scaffold->>Plan: Treat root as ordinary create run
end
Scaffold->>Plan: Print branch and remote visibility rows
Scaffold->>Remotes: _create_remotes(root_seen)
Remotes->>GitHub: Create or reuse root and legs
Remotes-->>Scaffold: Standing visibility per repository
Sequence diagram for unread reused-leg diagnosticssequenceDiagram
participant Scaffold as scaffold-project.py
participant Visibility as shape_materialize.repository_visibility
participant GitHub as GitHub gh CLI
participant Notes as Diagnostic notes
Scaffold->>Visibility: _reused_leg_visibility(leg)
Visibility->>GitHub: gh repo view leg --json visibility
GitHub-->>Visibility: visibility or unread result
alt visibility unread
Visibility-->>Scaffold: None
Scaffold->>Notes: _unread_legs_note(leg)
Scaffold->>Notes: free_plan_secrets_note(...)
else visibility available
Visibility-->>Scaffold: leg visibility
Scaffold->>Notes: Credential and Free-plan notes as applicable
end
Flow diagram for family init plan and holder credentialsflowchart TD
Init[family.py init] --> Plan[_print_init_plan]
Plan --> Local{--local-remote-dir}
Local -->|yes| LocalRows[Print visibility and branch rows]
Local -->|no| GitHubRows[Print gh repo create visibility and not-found token limits]
LocalRows --> Holder[_finish_holder_remote]
GitHubRows --> Holder
Holder --> Credential[Print members credential NOTE for every GitHub holder]
Credential --> PlanHint[Add Free-plan hint when applicable]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-10-18T18:55:03Z) RV969's independent review of #969 at Each section against its Direction:
SHOULD (not blocking):
NIT:
Tests, at the head, one at a time under a stub gh:
Mutants:
Merges:
Next: lane 3 runs a short fix round for SHOULDs 1-2 and NITs 1, 2, 4 and 5, and RV969 re-checks the delta. NITs 3 and 6 go to follow-ups. Lane: openreposhape-3 (openRepoShape-3) |
…s it could not read ends "cannot check them out", `init`'s members' NOTE names a member that is, or has a leg that is, private or internal, and a reused leg gh does not find is pinned as unread, so it is no longer taken at the run's visibility (#913) Fix round 1 on RV969's review of #969. - SHOULD 1: `_private_leg_note` returns first for a `--local-remote-dir` run and drops its three `not local and` guards, which brings its cognitive complexity under SonarCloud's 15. Nothing it prints changes. - SHOULD 2: `test_a_reused_leg_it_could_not_read_is_said_to_be_unread` has a row whose leg read is gh's not-found answer, which fails where `_reused_leg_visibility` takes that read at the run's visibility. - NIT 1: the same test counts the unread-leg NOTE in stdout alone, so the NOTE on stderr is caught. - NIT 2: a 115-character docstring line in test_free_plan_hint_reads.py is wrapped. - NIT 4: `init`'s members' NOTE names "a member you add that is, or has a leg that is, private or internal": a public member with private legs needs the credential too, since the holder's `validate` runs `--recursive`. - NIT 5: `_unread_legs_note` says "cannot check them out" for two legs. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
RV969 round 2 (lane 3's independent opus review) of #969's fix round 1, head f3802d6: READY. The delta (git diff e016994 f3802d6) touches only scaffold-project.py, scripts/family.py and the two PR test files. Each change is correct:
Tests on f3802d6, one at a time under a stub gh: test_plan_visibility_notes.py 16 passed; test_free_plan_hint_reads.py 62 passed; test_family.py 112 passed. Mutants: "unread-leg NOTE on stderr" is now killed by all four rows of test_a_reused_leg_it_could_not_read_is_said_to_be_unread; "not-found leg taken at the run's visibility" is now killed by its one-leg-not-found row. Merge: git merge-tree --write-tree f3802d6 origin/main (66177b6) is clean. CI: SonarCloud, package and tests-git-bash pass; the Linux tests job was still running at 19:14Z and must be green before landing. Lane: openreposhape-3 (openRepoShape-3) |
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 5 days by commenting |
…user cannot read is refused by name, `member-unreadable`, exit 2, so Python 3.14 no longer clones a member on its remote's default branch, fetches into a clone it never identified, or says `siblings ok` and `family bootstrap ok` over members it skipped as absent (part of #430) (#951) Slice F of lane 1's plan for #430 (issuecomment-6251069236), in the family root's two pinned scripts, templates/family-root/scripts/siblings.py and bootstrap.py. Each guard asked `Path.is_file()` or `Path.exists()` of a member's path. Python 3.12 and 3.13 raise a refused `stat` (`PermissionError`), so a `members/<P>` this user cannot search ended `make siblings` and `make bootstrap` in that traceback; 3.14 answers False to any `OSError`, so the path read as absent. Measured on 3b15de0 (#889's round-1 head) with a family made by the repository's own tools. Under a real mode on Python 3.12.3 every site below ended in a `PermissionError` traceback, exit 1. Under a 3.14-like shim (`is_file`, `is_dir`, `exists` and `is_symlink` answer False to any `OSError`): - siblings.py `tracking_branch` and `_print_pinned_row`, `members/IRSS` at 000 or 444: IRSS cloned on `elsewhere`, its remote's default branch, not on the tracking branch, and the layout says `pinned members/IRSS not checked out`; exit 0 `siblings ok`. - `project_id`, a clone's `project.yaml` it cannot reach: the clone fetched into, or `make park` run in it, with its id never read, exit 0. - `run_member_bootstrap`: "no scripts/bootstrap.py; cloned and left as it is", its legs never placed, exit 0. `dispatch`'s `Makefile`: "has no Makefile". - bootstrap.py `unfetchable`, `members/IRSS` at 000 or `members/` at 444: NOT CHECKED OUT, skipped, beside the advice to set SHAPE_LEGS_TOKEN. `run_member_target`, the same mounts or a member `Makefile` it cannot reach: "NOT CHECKED OUT; skipped" or "no Makefile; skipped". Both exit 0 `family bootstrap ok`. - `members_of`, a `family.yaml` it cannot reach: `family-manifest-missing`, "does not exist". Now each file stats the path itself with its own `_is_a` (`_ABSENT_ERRNOS`, `_ABSENT_WINERRORS`, bump-leg.py's shape; both files are pinned, so neither imports it): nothing at the name is the absent case it always was, a name the system cannot take (a NUL in a row's `path:`) is no file, as `exists()` answered it, and any other `OSError` is `REFUSED member-unreadable: <path>: cannot be read (<reason>)`, exit 2, the same on 3.12 and 3.14. A permission gets validate-pins.py's `chmod u+r` / `chmod u+rx` advice (bootstrap.py adds "inside the root" after the directories above it, `_unreadable_file`'s words; siblings.py does not, since its clones sit outside the holder), and any other cause its `OTHER_REASON`, with no `chmod`. `family.yaml` is `family-manifest-unreadable`, the code a `family.yaml` that is no mapping already has. - siblings.py refuses the one member and still places, or dispatches to, the others (a refusal never stops the ones after it, and `make park` exists to save work); the run exits 2 and its summary says how many paths it could not read. A refused `scripts/bootstrap.py` says the clone was made and to run `python3 scripts/bootstrap.py` in it, since a second `make siblings` only fetches. - bootstrap.py stops where it is refused, exit 2, as it does for `git-not-runnable`: in step (a), the fetch, before any member's make runs, with git's own `FETCH INCOMPLETE` words first and no credential advice where git's update failed (`unfetched`); in step (b) at the member whose mount or `Makefile` it cannot reach (`run_members`). `main` catches the refusal from either once, stdout flushed first. Rounds and review. The writer is lane 4's W430F; the reviewer is lane 5's rv951. - Round 0, 528adbf, stacked on #889's 29083a5. Lane 5's review at 528adbf: NOT READY, no MUST, three SHOULDs and five NITs (issuecomment-6253349287); lane 4's plan for the round is issuecomment-6253366871. - Round 1, 3c5f2ea, a fast-forward on 528adbf (issuecomment-6254509055). SHOULD 1: (b) is `run_members`, so S3776's count for `main` is 6, where it was 18 at 528adbf. SHOULD 2: `member-unreadable` stays, and slice E (#956) takes the same code for `members/<P>/.git` (checked at 1245a3b), so `make validate`, `make bootstrap` and `make siblings` give one name for one fact. SHOULD 3: a NUL in a name is no file. NIT 4: the placement summary's words are tested. NIT 5: the reason `parent_folder_warning` is left alone is corrected. NIT 6: git's own words come before step (a)'s refusal. NIT 7: the errno tests are three, with true names. NIT 8: bootstrap.py says "inside the root". - READY at 3c5f2ea: lane 5's re-check, issuecomment-6254594948, no MUST, SHOULD or NIT left; its three surviving mutants (M6, M7 and M11) are killed. - Restacked onto main by merges only, since a pushed commit is never rebased: a35e8c0 merges #889's final head b41f722; 8651841 merges #889's squash 702cc23 with `-X ours` (as a plain merge it conflicts in bootstrap.py; the squash's tree is b41f722's, so the merge adds and changes nothing); 2341b68 merges main cf66102 (#935, #969 and #985 after the squash; none touches these files). Lane 4's reply is issuecomment-6266695040. - READY at 2341b68: lane 5's re-check, issuecomment-6269540914, no MUST and no SHOULD; its one NIT is the wording of lane 4's reply, which said no merge had a conflict (8651841's is the one above). #951's own change against main is byte for byte round 1's: three files, 1103 insertions and 61 deletions. tests/test_family_siblings_bootstrap_unreadable.py (new, 57 tests). Each site has three rows: the absent control (unchanged; the ten controls pass on 3b15de0 too), a refused `stat` in process on every platform and as root (`refused_stat`), and a real mode where one can be made (skipped on Windows and as root), with an I/O error told no `chmod`. Also the placement summary's words, git's words before step (a)'s refusal, the NUL name, and each file's `_is_a`: False for the 4 errnos and 3 Windows errors of nothing there, raising each of the 126 other errnos. The helpers are imported from tests/test_bump_leg_unopenable.py and tests/test_doctor_unreadable_pin.py, none copied; conftest.py is not touched. Round 0's file (35 tests) was 25 failed and 10 passed on 3b15de0; all 57 pass. Runs (lane 4's on Python 3.12.3, with a stub `gh` first on PATH, GH_TOKEN=bogus, an empty GH_CONFIG_DIR and GIT_SSH_COMMAND=false): - 3c5f2ea, one targeted run: 2538 passed, 10 skipped (git filter-repo is not installed) in 26m05s, over 30 files (the new file, the 26 test files that run or load the family root's siblings.py or bootstrap.py, and the trio: tests/test_repo_hygiene.py, tests/test_cli_reference.py and tests/test_agents_md_codes.py) and the 19 tests of tests/test_shape_doctor.py that load or run them. - 2341b68: the new file, all of tests/test_shape_doctor.py, tests/test_scaffold_leg_path_collisions.py and the trio: 772 passed, 7 skipped in 13m44s. - Lane 5's, on git 2.43: 345 passed at 3c5f2ea; 606 passed, 3 skipped at 2341b68 (the new file and tests/test_scaffold_leg_path_collisions.py, 161; tests/test_shape_doctor.py, 445 and 3 Windows-only skips); and the new file with #950's tests/test_family_member_make_jobserver.py on a merge with main c7a3ff1, 100 passed. 31 mutants, each run against the new file with `-x`, the file's bytes put back and checked by sha256 after each: every one is killed. Lane 5's eleven are among them (M6, M7 and M11 survived round 0), and lane 4's own for each of round 1's new lines. Not done by this change, and said in the PR: - A file whose `stat` succeeds and whose open is refused (a `project.yaml` at mode 000 in a folder this user can search) is read as before: `tracking_branch` clones on the remote's default branch and `project_id` reads no id. It is no absence read, and it predates #430. - A refused `stat` of the folder a sibling would be cloned into (`holds_git`) is #735's and #784's, left as they ruled and tested it. `parent_folder_warning`'s `wanted.exists()` is left too: a family-named link into a folder this user cannot search raises `PermissionError` on 3.12 and says "it is a link to nothing" on 3.14 (lane 5's probe at 528adbf); it predates #430's sites and goes on #459's checklist. - No Python 3.14 interpreter was run: the 3.14 half is `refused_stat` in process and a shim on the command line. Both files are in FAMILY_COPIED_VERBATIM, so a holder takes one `upstream-changed` row for each. Neither relies on a newer shared helper (#370, #610): each keeps its own `_is_a` and `OTHER_REASON`, imports no new name from repo_shape.py (which is untouched), and siblings.py imports no new name from bootstrap.py, so either taken alone runs beside the other's older copy. Part of #430 (slice F; lane 1's plan, issuecomment-6251069236). #430 stays open for its other slices. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… hint no longer calls an internal root private, adopt's docstring says which of its notes changed, and `prepare`'s docstring has no 104-character line (#994) (#1038) Three statements in the source are made true. The Free-plan secrets hint told an internal root on a plan read as `free` that it "is private". It now says the root "is internal, which this hint treats as private", the words the issue's Direction gives. Every other visibility and plan answer is unchanged. `scripts/repo_shape.py` is untouched, so no project re-pins. The case should not arise, since internal visibility needs an enterprise, and a comment at the branch says so. The docstring of adopt's `_free_plan_hint_by_the_root` said adopt's words were unchanged. It now names the two cases that changed: an internal root with an unread plan (#969), and an internal root on a Free plan (this change). The 104-character line in the docstring of `prepare` in `src/openreposhape/standard.py` is re-wrapped, with the same words. The Free-plan hint's rows now pin the internal case for the scaffold, adopt and `family.py init` alike. Closes #994 Lane: openreposhape-3 (openRepoShape-3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Part of #913. Every section is done but U039, which needs a ruling (see "Left out"), so this does not close the issue.
What changed, by section
An unread visibility is taken as the run's, and the root is read twice (U163, U167; L2-92, L2-96)
shape_materialize.repository_visibility_answeris the onegh repo view --json visibilityreader now. It answers the visibility,REPOSITORY_NOT_FOUNDfor gh's own not-found answer (gh_said_not_found, exit 1 with the words on stderr, the reading scaffold-project.py andfamily.py init: the dry runs skip the target-exists and folder checks, and anygh repo viewfailure reads as "does not exist" #609 givesrefuse_unless_not_found), or None for any other failure.repository_visibilityis that reading with not-found taken as unread, sofamily.pyand its callers see what they saw before. It runs the command withsubprocess.run, asorganisation_plandoes, because the reading is of stderr alone._leg_visibilityreads the root once. Where the read failed (not where gh does not find the root, which is the ordinary--reuse-empty-reporun), it prints a leg twin ofROOT_VISIBILITY_UNREADBEFORE the plan: with no--visibility, that a leg this run creates is created private and does not inherit the root's, and to pass--visibilitybefore any leg exists; with one, that it cannot say whether the flag agrees with the root. Both namegh repo view <root> --json visibility._leg_visibilityreturns the root's read with the legs' visibility, and_create_remotestakes it for a reused root instead of asking GitHub a second time._reused_leg_visibilityreturns None for a leg whose read failed (it returned the run's visibility)._private_leg_notenames each such leg in a NOTE of its own (_unread_legs_note: the question, what a private or internal answer needs, and the command that answers it), goes on naming the legs it could read as before, and asks the Free-plan note when a leg is closed OR unread, since an unread leg may be closed.--visibilitytoo, because the issue's Goal says a failed read is said before anything is created; and the unread-leg NOTE follows L2-92's own Direction ("says which leg's visibility it could not read ... and goes on by the legs it could read").Free-plan words for an internal root (U164; L2-93)
free_plan_secrets_noteanswers an internal root whose organisation's plan is unread withFREE_PLAN_UNREAD_INTERNAL: GitHub gives internal visibility only to a repository of an organisation an enterprise account owns, which is not on the Free plan, so the plan is not what the hint hangs on. The NOTE is printed where it was and the plan is still asked, so nothing but the words changes. Adopt gets the same words through the shared function.No credential NOTE for a public holder (U166; L2-95)
initprints the credential NOTE for every holder on GitHub, public, private, internal or unread, worded about the members: a private or internal member a person adds needs a way for the holder, public or not, to read it. It went by the holder's visibility and gave that as the reason.initone;addis lane 4's ground.Test gap: the
not-found-on-stdout-alonerow (U168; L2-97)gh: Not Found (HTTP 404)(the file'sNOT_AN_ORGANISATION) with a newline, and the body row is as it was. Mutant M15 below is killed by it; main's row lets the same mutant live (control run below).The plan names no tracking branch, and no visibility in local mode (U010; AT-scaffold N3)
branchrow (<branch>, the tracking branch each of the three is created on) and, under--local-remote-dir, avisibilityrow (<visibility>, which project.yaml records; a bare repository on disk has none). Against GitHub the visibility stays in thegh repo create --<visibility>row. Setup's step (5) shows the same plan.init's plan gets the same two rows (family.yaml,the holder is created on), since the issue's Goal namesinit's plan and lane 4 agreed lane 3 writes scaffold-project.py,family.py initand shape_materialize.py: the plan and the visibility NOTEs go by values GitHub did not give, misword an internal root, skip a public holder's credential NOTE, and name no tracking branch #913'sinithalf.A private repository the token cannot see reads as absent (U060; L2-61)
remotesrow against GitHub says the run creates each repository gh does not find, and two lines under it (NOT_FOUND_WITH_THIS_TOKEN, shared by the scaffold andinit) say that with this token a private repository it cannot see is not found either, andgh repo createthen fails for it.Left out
visibility:records" (U039) is out of scope and untouched:project.yaml's andfamily.yaml'svisibility:are still filled from the run's--visibility, and the template comments are unchanged.addoption (a NOTE fromaddfor a private or internal member):addis lane 4's ground inscripts/family.py.adopt-project.py's docstring that namesFREE_PLAN_UNREAD(around line 11634) is not in this change's files; it stays true of a private root.Tests
Each ran under a stub
ghfirst on PATH that exits 97,GH_TOKEN=bogusand an emptyGH_CONFIG_DIR, astimeout 2400 /usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-<name> -o tmp_path_retention_policy=failed <files>, one at a time. The first eleven ran on the commit before its rebase onto450279ca(the same diff; main's two new commits touch neither these files nor the code they run); the last two ran on the rebased head.tests/test_plan_visibility_notes.py(new)tests/test_free_plan_hint_reads.pytests/test_scaffold_reuse_seeded_legs.pytests/test_dry_run_asks_remotes.pytests/test_dry_run_parity.pytests/test_reuse_visibility.py tests/test_free_plan_hint.py tests/test_adopt_free_plan_hint_root.pytests/test_family.pytests/test_scaffold_e2e.py tests/test_scaffold_pin_and_reuse.py tests/test_tracking_branch_ref.py tests/test_family_tracking_branch.py tests/test_family_init_values.pypytest.skips oftest_family_init_values.py)tests/test_setup_project_py.py tests/test_github_limits_init_check_org.py tests/test_github_name_limits.py tests/test_scaffold_manifest_values.pytests/test_agents_md_codes.py tests/test_cli_reference.py tests/test_update_add_codes_documented.py tests/test_repo_hygiene.pytests/test_no_auto_maintenance.pytests/test_plan_visibility_notes.py tests/test_free_plan_hint_reads.py tests/test_scaffold_reuse_seeded_legs.py tests/test_ruleset_hint_tracking_branch.pytests/test_no_auto_maintenance.pytests/test_shape_doctor.pywas not run: neithershape-doctor.pynor a helper it imports changed.Mutants
Each was applied to the committed head, run against the named tests with
-k, and put back withgit checkout. All 16 were killed._reused_leg_visibilityreturns the run's visibility for an unread legtest_plan_visibility_notes.py::test_a_reused_leg_it_could_not_read_is_said_to_be_unread(3 of 3 rows)[private-legs-under-a-public-run]test_a_root_it_could_not_read_is_said_before_the_plan[default],[chosen];test_a_real_run_reads_the_reused_root_once[unread]repository_visibility_answerreturns None)test_a_root_gh_does_not_find_or_reads_is_not_said_to_be_unread[not-found];test_free_plan_hint_reads.py::test_a_visibility_read_is_one_of_three_not_found_or_unread[not-found]_create_remotesreads the root againtest_a_real_run_reads_the_reused_root_once[read],[unread]branchrowtest_the_plan_on_disk_names_the_branch_and_the_visibility[defaults-scaffold],[chosen-scaffold];test_the_plan_on_github_says_not_found_is_what_this_token_sees[scaffold]visibilityrowtest_the_plan_on_disk_names_the_branch_and_the_visibility[defaults-scaffold],[chosen-scaffold]initbranchrowfamilyrows (3)initlocalvisibilityrowtest_the_plan_on_disk_names_the_branch_and_the_visibility[defaults-family],[chosen-family]test_the_plan_on_github_says_not_found_is_what_this_token_sees[scaffold]init's plantest_the_plan_on_github_says_not_found_is_what_this_token_sees[family]FREE_PLAN_UNREADtest_free_plan_hint_reads.py::test_the_note_goes_by_both_answers[internal-unread-plan]init's NOTE only for a private or internal holdertest_init_notes_the_members_credential_for_every_reused_holder[public],[visibility-502],[visibility-404];test_init_asks_nothing_of_a_holder_it_creates[public];test_init_takes_a_holder_gh_cannot_be_asked_about_as_unreadgh_said_not_foundsearches stdout tootest_the_plan_is_free_other_or_unread[not-found-on-stdout-alone];test_a_visibility_read_is_one_of_three_not_found_or_unread[not-found-on-stdout-alone]repository_visibilitykeeps not-foundtest_a_visibility_read_is_one_of_three_not_found_or_unread[not-found]Control for M15: main's
tests/test_free_plan_hint_reads.pywith the same mutant,-k plan_is_free_other_or_unread: 11 passed, so main's row did not catch it.Merges
git merge-tree --write-treeof this head (e016994872) with each, all clean (exit 0, no conflicted file):origin/mainat450279ca;family-manifest-invalidnames the value on the line it refuses,--org is '@x', and gives the reader's remedy, not the validator's (#929) #960 (family.py init: the whole-manifest YamlError refusal names no flag, and its remedy says the words are the validator's own when they are the reader's #929) atc6700b7f, family.py add: a commit that records the member's gitlink anywhere butmembers/<Project>is taken back and refused, naming the path git chose, andmembers: null,Null,NULLor~takes the first member asmembers:does (#920) #957 (family.py add: the commit is never checked for a gitlink at exactlymembers/<P>, andmembers: nullis refused as a missingmembers:line #920) at37faf298, family.py: a holder, a member or its object store this user cannot read is refused by name, exit 2, on every Python, neverfamily-root-missing,member-uninitializedor "not on disk", and no rollback removes past it (#430) #955 (EveryPath.is_file()/is_dir()/exists()guard in the shipped tools answers False to any OSError on Python 3.14 (3.12 and 3.13 raise PermissionError), so a directory this user cannot search reads as ABSENT: validate-pins.py skips its workflow@<sha>check, update-shape.py sayscopy-missingandupdate-pin-missing("adopt the project") for files that are there, the doctor says DRIFTED (5 copy-missing), and bump-leg.py's remaining guards, and every other tool's, skip or misreport instead of refusing by name #430 slice G) at991990cc, family.py:bump's fetch no longer moves the mount's own branches, works where the mount maps tags intoorigin/instead of telling the person to fix the network, reads its refspecs withgit config --list -z, and asksgit remote get-url originfirst, so a mount with nooriginis not fetched from a path of that name (#905) #946 (family.pybump: its fetch can force-move a local branch through the mount's own refspec, fails on a tags-into-origin/refspec with a network note, splitsgit config --listvalues, and fetches a path namedorigin#905) at5d5d06bb: the open PRs that touchscripts/family.pyorscripts/shape_materialize.py; none touchesscaffold-project.pyor the two test files;l3-907-926-setup-step4-linksat8c5d6051,l3-917-yaml-readeratd3a2ee61,l3-939-941-package-standardatc1de4c86.W909's, W911's and W915's branches in
scaffold-project.pywere not pushed when this was opened, so they were not merged against. This change's hunks there areremote_visibility,_leg_visibility,_print_plan_actions,_create_remotes,_reused_leg_visibility,_private_leg_note, a new_unread_legs_note, the scaffold's shape_materialize imports, and the two_scaffoldlines that call_leg_visibilityand_create_remotes. Inscripts/family.pythey are_print_init_plan,_finish_holder_remoteand one import after the GitHub-limits group.Fix round 1 (head
f3802d6f)RV969 found the head
e016994872READY: no MUST, two non-blocking SHOULDs and six NITs. This round makes SHOULDs 1 and 2 and NITs 1, 2, 4 and 5, in one commit on the same branch, as a fast-forward. Behaviour changes only in the two NOTEs' words (NITs 4 and 5)._private_leg_notereturns first for a--local-remote-dirrun and drops its threenot local andguards, which was SonarCloud's S3776 at cognitive complexity 16 where 15 is allowed. Counted by hand under the rules that give the old text 16 (eachif, eachand/orsequence, each conditional expression, nesting added), the new text is 14. A local run still prints nothing from it: the two local-remote tests oftest_scaffold_reuse_seeded_legs.pyfail on the mutant that drops the return (below).test_a_reused_leg_it_could_not_read_is_said_to_be_unreadhas a fourth row,one-leg-not-found, whose spec leg's--json visibilityread is gh's own not-found answer,visibility_errors={f"{PROJECT}-spec": "GraphQL: Could not resolve to a Repository"}, under--visibility public. The error text is a column of the table now (error,BAD_GATEWAYin the three rows that had it).result.stdoutalone, so the NOTE on stderr no longer passes.tests/test_free_plan_hint_reads.pyis wrapped.scripts/family.py's members' NOTE says "a member you add that is, or has a leg that is, private or internal" where it said "a private or internal member you add": a public member with private legs needs the credential too, since the holder'svalidateruns--recursive. Onlytests/test_free_plan_hint_reads.py'smember_notepinned the old words, and it is changed with it._unread_legs_noteends "cannot check them out" for two legs and "cannot check it out" for one (it said "it" for both). The test'sunread_legshelper builds the same.adopt-project.py's docstring) and NIT 6 (shape_materialize.py:1970) are lane 3's follow-ups, outside this PR's files.Tests, under the stub
gh,GH_TOKEN=bogus, an emptyGH_CONFIG_DIRandGIT_SSH_COMMAND=false, one at a time, as above, on the committed bytes:tests/test_plan_visibility_notes.py tests/test_free_plan_hint_reads.py tests/test_scaffold_reuse_seeded_legs.pytests/test_family.py tests/test_family_init_values.pytests/test_dry_run_parity.pytests/test_agents_md_codes.py tests/test_cli_reference.py tests/test_repo_hygiene.pytests/test_no_auto_maintenance.pyMutants of this round, each applied to the committed text, run with
-k reused_legon the named files, and put back:test_a_reused_leg_it_could_not_read_is_said_to_be_unread, all 4 rows_reused_leg_visibilityreturnsargs.visibilityfornot-found)test_a_reused_leg_it_could_not_read_is_said_to_be_unread[one-leg-not-found](the other 3 rows pass, as they should)[private-legs-under-a-public-run]and[public-legs-under-a-private-run]_private_leg_notewithout its early return for a local remotetest_three_empty_repositories_are_all_reused_with_the_flagandtest_legs_holding_the_seed_are_reused_at_their_commits(test_scaffold_reuse_seeded_legs.py): the local run then asksgh api orgs/testorg --jq .plan.nameinit's members' NOTE in its old wordstest_init_notes_the_members_credential_for_every_reused_holder, all 5 rowsMerges,
git merge-tree --write-treeof the new headf3802d6fd452ffbd7deb9b92f4f286342ba2c8abwith each, all clean (exit 0, no conflicted file):origin/mainat293ef2a5, #971 (l3-915-scaffold-git-prompts) ate40af32d, and #972 (l3-911-930-scaffold-init-flags) at0094731b.Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code