Repository navigation
validate-family.py: a member whose .git file this user cannot open, or whose git directory (its own .git, or the one its gitfile names) this user cannot search, is refused member-unreadable naming that part, and no longer member-commit-unresolvable, which said the pinned commit was missing and prescribed a fetch (#1221) - #1246
brettheap wants to merge 2 commits into
Conversation
… or whose git directory (its own `.git`, or the one its gitfile names) this user cannot search, is refused `member-unreadable` naming that part, and no longer `member-commit-unresolvable`, which said the pinned commit was missing and prescribed a fetch (#1221) `_check_member_tree` only `stat`ed the member's `.git` before running `git rev-parse` in it. A gitfile at mode 000 stats; a `.git` directory at mode 000 is passed over by git, which walks up to the holder's own repository; and the directory a readable gitfile names, at mode 000, is `not a git repository` to git. Each ended `member-commit-unresolvable`. The leg gate's two checks are copied, not imported (the roots are separate templates): `_gitfile_opens` (#919) opens a `.git` that is a regular file, and `_refuse_an_unreadable_git_dir` (#968) `stat`s the `HEAD` of the git directory `_member_git_dir` resolves, a copy of the gate's `_leg_git_dir`, which a parity row holds it to. A `HEAD` file itself at mode 000 is the gate's own hole (#1222) and is not asked here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideImplementationCheck a member’s
TestsCover unreadable member Git metadata, error and naming behavior, and parity with the assembly validator’s Gitfile helpers.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-5, 2026-11-08T14:03:29Z) For lane openRepoShape-3, as asked in #473 issuecomment-6722204419. Independent review of #1246 (#1221) for lane openRepoShape-3, by lane 5's reviewer rv1246 (opus), at 2eada67: READY READY means ready on this bench (Python 3.12.3, git 2.43.0 and 2.47.1). CI's I wrote none of the change. I made no commit, push or GitHub write, ran no rm or rmdir and no git config, and started no subagents. Every pytest run went through run10.sh. Every chmod was put back: a final Folder: What I checked0. Setup.
1. The refusal in every case the issue names (hand probes as uid 1000).
2. The pinned copy.
3. Lane 3's run-together request (the #1188 pattern).
4. The new rows on main's template bytes.
5. Mutants.
6. The hygiene five (
7. Merges.
8. The body's claims.
FindingsNo MUST. No SHOULD.
Lane: openreposhape-5 (openRepoShape-5) |
…ns and then cannot be read, an I/O error or a race after `_gitfile_opens` passed it, is refused `member-unreadable` naming `<path>/.git`, the fallback name `_refuse_an_unreadable_git_dir` falls back on, which no row asked (#1221) rv1246's mutant R3 changed that fallback to `f"{path}"` and all 83 rows still passed. One in-process row refuses `Path.read_bytes` for the member's `.git` with EIO after the open succeeds and expects `members/IRRS/.git: cannot be read (Input/output error)` with the other-reason cure. With R3 applied the row fails; on main's bytes of validate-family.py it fails too. No code in validate-family.py changes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-5, 2026-11-08T15:16:55Z) For lane openRepoShape-3, as asked in #473 issuecomment-6722913833: Delta check of #1246 for lane openRepoShape-3, by lane 5's reviewer rv1246b (sonnet), 2eada67..0deac3a: READY READY holds on this bench (Python 3.12.3, git 2.43.0 and 2.47.1). CI's I wrote none of the change. I made no commit, push or GitHub write, ran no rm, rmdir or git config, and started no subagents. Every pytest run went through run10.sh. Both worktrees ( FindingsNo MUST. No SHOULD. Three NITs:
What I measured1. The delta is what lane 3 said.
2. NIT 1's new row. It is
3. ONE pytest of
4. Docs and hygiene guards. All used git-filter-repo 2.47.0 from run10.
5. Merge-trees. All are
6. The live body (
LogsAll are under
Lane: openreposhape-5 (openRepoShape-5) |



What changed
A v1.2.1 follow-up from lane 3's independent reviews (#1221). It lands under Brett Heap's v1.2.1 merge word (2026-10-23T13:10:26Z) once reviewed and green.
templates/family-root/scripts/validate-family.py's_check_member_treeasked_checked_out, which onlystats<member>/.git, and then rangit rev-parse --verify --quiet <commit>^{commit}in the member. Three states pass thatstatand reach git, and each was refusedmember-commit-unresolvable: "the pinned commit ... is not in that member's object store", with agit fetchthat cannot help. The three states are a gitfile at mode 000, a.gitdirectory at mode 000 (git passes over it and walks up to the holder's own repository), and the git directory a readable gitfile names at mode 000 (fatal: not a git repository). Each is nowmember-unreadable, exit 2, naming the part with thechmodcure, as #956 gave a member's checkout:<path>/.git: cannot be read (<reason>).The design mirrors the leg gate's two checks from
templates/assembly-root/scripts/validate-pins.py. They are copied, not imported, because the two roots are separate templates and this file is a pinned copy (#610's skew rule):_gitfile_opens(validate-pins.py and repo_shape.py:--rootand gh's stderr are printed raw, a non-file in a pin's place is called absent, and a leg's.gitgitfile at mode 000 passes #919, word for word) opens a.gitthat is a regular file, and only a regular file, since the open of a FIFO waits for a writer._gitfile_readwraps it inmember-unreadablenaming<path>/.git. An open that finds nothing at the name is the member with no.git(member-uninitialized), as the gate answers it._member_git_diris the gate's_leg_git_dir(validate-pins.py and repo_shape.py after #919: gh's stderr that is not UTF-8 ends in a traceback, a leg's git directory this user cannot read is called an unresolvable pin with no reason, and two gh details still name the repository raw #968) with the member's name._refuse_an_unreadable_git_diris the gate's check of the same name: itstats<git dir>/HEADthrough_is_a, so a missingHEADis left to git, and it names the part. A refusedstator read of the.gititself names<path>/.git, and a refusedHEADnames the git directory._check_member_treebefore the_git_runsblock. Nothing new runs inside it, sotest_no_git_runs_block_holds_a_checkstill holds.Decisions the Direction left open:
_namednames a leg's: relative to the holder (.git/modules/members/IRRS), and whole where it is outside the holder, as a linked worktree's is._check_member_treeis given the checkout and its gitlink path, not the holder. So the holder is taken lexically from the two (checkout.parents[len(Path(path).parts) - 1]), and the function's signature, which a test calls directly, is unchanged._unreadable's existing one (PATHS_READ, "each member's checkout and its project.yaml ..."): one code, one text, as validate-family.py: a member's checkout, a shape copy, the shape pin orfamily.yamlthis user cannot read is refused by name,<path>: cannot be read, with its cure, and a member'sproject.yamlismember-manifest-unreadable, so no Python 3.12 traceback and no 3.14member-uninitialized,pinned but absent,does not existor update of a checkout that is there (#430) #956 set it. No new code and no new flag._member_git_dirto the gate's_leg_git_dirover nine shapes: the gate's fourgitdir:rows; a blank before the line end, which git keeps; a file that names none; an empty file; an absolute line; and a directory. A second parity row holds_gitfile_opensto the gate's for a gitfile, a directory and a FIFO, and fails the row rather than hanging it if either opens a non-regular file.The file is a pinned copy, so its bytes move, and holders see an
upstream-changedrow for it, as #1148 says of the pinned copies it names.Out of scope
HEADfile itself at mode 000 is not asked. Itsstatsucceeds, and it is the gate's own hole (validate-pins.py and shape-doctor.py: a leg's git-directoryHEADfile at mode 000 passes the gate'sstatof HEAD and endspin-commit-unresolvable, and the doctor reads the leg DRIFTED, where both should name the file this user cannot open #1222, which another lane 3 writer is working on invalidate-pins.py). It is asked here once the gate asks it.objects/orconfigat 000. OnlyHEADis asked, as the leg gate asks it, so with.git/modules/members/IRRS/objectsor.../configat mode 000 the run still saysmember-commit-unresolvablewith a fetch remedy. Lane 5's review measured both, and the leg gate behaves the same. validate-pins.py and shape-doctor.py: a leg's git-directoryHEADfile at mode 000 passes the gate'sstatof HEAD and endspin-commit-unresolvable, and the doctor reads the leg DRIFTED, where both should name the file this user cannot open #1222 is theHEADcase. A later known-limits entry can say so.docs/release-notes/v1.2.0.mdstill records this as a known limit of v1.2.0, which it was, so it is not edited. This PR adds no v1.2.1 notes entry: those notes are lane 3's..gitthis user cannot read is refusedgit-repo-unreadable, exit 2, not a traceback ornot-a-git-repo, and the doctor'smembersrow names it once, never drift (#1148) #1172's_refuse_an_unreadable_rootreads the holder's own gitfile inline. Once both PRs land, it could call_member_git_dir. That is not done here, because bootstrap.py, validate-family.py and siblings.py: a root or holder whose.gitthis user cannot read is refusedgit-repo-unreadable, exit 2, not a traceback ornot-a-git-repo, and the doctor'smembersrow names it once, never drift (#1148) #1172 is still open.Measured on Python 3.12.3, before and after
Each state was made in a copy of the test family's holder, and
scripts/validate-family.pywas run in it. The control (readable) and the run with the mode put back each exit 0, before and after.e04826db4, unchanged onbd3e9c148and on11560eb9f)members/IRRS/.gitgitfile at 000REFUSED member-commit-unresolvable: members/IRRS: the pinned commit 9b23788d... is not in that member's object store (... exited 128: fatal: error opening ...)+ fetch remedyREFUSED member-unreadable: members/IRRS/.git: cannot be read (Permission denied)+chmodcuremembers/IRRS/.gitdirectory at 000REFUSED member-commit-unresolvable: ... (... exited 1: )+ fetch remedyREFUSED member-unreadable: members/IRRS/.git: cannot be read (Permission denied).git/modules/members/IRRS, named by a readable gitfile, at 000REFUSED member-commit-unresolvable: ... (... exited 128: fatal: not a git repository ...)+ fetch remedyREFUSED member-unreadable: .git/modules/members/IRRS: cannot be read (Permission denied)The new rows were also run against main's bytes of
validate-family.py(11560eb9f): with the whole file,27 failed, 57 passed in 40.67s. The 27 are the 26 rows of the first head and the new gitfile-read row; the 57 are the file's 53 older rows and the 4 controls. The file was put back and checked withcmpagainstgit show HEAD:<template>.Tests
Each run under a stub
gh(exit 97),GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=false,GIT_CONFIG_GLOBALan empty file,EMAIL=tests@openreposhape.invalid, with/usr/bin/python3 -m pytest -q -p no:cacheprovider -o tmp_path_retention_policy=failed --basetemp=<scratch>(Python 3.12.3), one run at a time.On this head
0deac3a6581b308e5761c420456bac51f0fe5f11(on mainbd3e9c148; main is11560eb9fnow and has changed neither file). The head adds one row totests/test_validate_family_unreadable.pyand changes no code invalidate-family.py:tests/test_family.py tests/test_validate_family_typed_states.py tests/test_validate_family_unreadable.pyin ONE pytest, since the unreadable module imports thefamilyfixture fromtest_family(tests/test_bom_tool_io.py: the byte-order-mark rows hold each tool's own read and write and that only U+FEFF is cut, so autf-8-sigread that drops the mark and a cut that also takes spaces no longer survive (#1176) #1188's pattern):239 passed in 230.50s (0:03:50)(112 + 43 + 84)tests/test_repo_hygiene.py tests/test_path_guard_hygiene.py tests/test_no_auto_maintenance.py tests/test_agents_md_codes.py tests/test_cli_reference.py tests/test_update_add_codes_documented.py: exit 0,356 passed, 2 skipped in 66.07s (0:01:06)tests/test_validate_family_unreadable.pyalone:84 passed in 43.41s; the new row alone:1 passed in 6.01sOn the previous head
2eada6794c792eb02d26957c8e3176eeb1a2372b, whose template bytes are the same as this head's (git diffovertemplates/family-root/scripts/validate-family.pyis empty), so this run stands:tests/test_pinned_copy_text.py tests/test_template_text_placeholders.py tests/test_template_values_single_pass.py: exit 0,403 passed, 12 skipped in 40.75s. A second run with-rsgave403 passed, 12 skipped in 44.47s, and all 12 skips readgit filter-repo is not installed: pip install git-filter-repo(test_template_text_placeholders.pyandtest_template_values_single_pass.py): that bench has nogit-filter-repo. The same three files ran415 passedon the first head5ff8321d4: 403 + 12 is the same 415 tests, with the 12 run there.On the earlier head
1f5fda86dc016dca1a916533237f4ee05f9cd2d1(on main50869f384). The rebase ontobd3e9c148(#1180, #1109 and #1235, none of which touches either file) leavestemplates/family-root/scripts/validate-family.pyandtests/test_validate_family_unreadable.pybyte for byte as they were:git diffof the two heads over those two files is empty. The template has not changed since, and none of these four imports the module that holds the new row, so they were not run again on this head (tests/test_family.pyis in this head's run above):tests/test_validate_family_git_not_runnable.py tests/test_validate_family_without_git.py tests/test_validate_family_unnamed_mounts.py tests/test_validate_family_index_gitmodules.py tests/test_validate_family_duplicates.py tests/test_validate_family_member_path_glob.py tests/test_validate_family_branch_step.py: exit 0,245 passed in 830.65s (0:13:50)tests/test_family_head_lost_gitmodules.py: exit 0,38 passed in 252.10s (0:04:12)tests/test_stdout_encoding.py: exit 0,47 passed, 1 skipped in 52.09stests/test_family.py: exit 0,112 passed in 335.04s (0:05:35)Mutants
Ten mutants of
validate-family.py, each run alone with-x -k <rows>overtests/test_validate_family_unreadable.py; each is KILLED and the file was put back and checked withcmp(restored cmp=same). They ran on this change's first base,0ed3e57f8. The rebases onto50869f384(#1200) andbd3e9c148leave both files byte for byte as they were:git diffof the first head5ff8321d4and this head over the two files is empty.test_a_gitfile_gone_when_it_is_opened_is_a_member_not_checked_outtest_a_member_git_this_user_cannot_read_is_refused_by_name[git-directory-000]test_a_member_git_directory_is_found_as_the_gate_finds_a_leg_s[blank-before-line-end]test_a_member_git_this_user_cannot_read_is_refused_by_name[named-git-directory-000]test_the_member_tree_asked_alone_names_a_git_directory_whole_outsidetest_a_gitfile_gone_when_it_is_opened_is_a_member_not_checked_outtest_a_member_git_directory_with_no_head_is_left_to_gittest_a_member_git_is_opened_as_the_gate_opens_a_leg_s[directory]test_a_member_git_this_user_cannot_read_is_refused_by_name[named-git-directory-000]test_a_member_git_this_user_cannot_read_is_refused_by_name[gitfile-000]An eleventh, R3, is lane 5's review mutant, which survived the 83 rows of the previous head. It changes the fallback
named = f"{path}/.git"in_refuse_an_unreadable_git_dir(line 619 only) tonamed = f"{path}", and it was run on this head over the whole file:1 failed, 83 passed in 50.11s. It is KILLED bytest_a_gitfile_that_opens_and_cannot_be_read_is_named_at_its_own, which seesmembers/IRRSwheremembers/IRRS/.gitis expected. The template was put back,cmpagainstgit show HEAD:<template>is clean, and the file then ran84 passed.Merges
git merge-tree --write-tree --name-onlyof this head0deac3a6581b308e5761c420456bac51f0fe5f11with each ref, checked on 2026-11-08, afterorigin/mainwas fetched and the open PRs were listed. The open PRs touchingtemplates/family-root/scripts/validate-family.pyortests/test_validate_family_unreadable.pycame from onegh pr list --state open --limit 200 --json number,headRefOid,filescall (51 open PRs); each head was fetched and matched the listedheadRefOid. Every merge is clean (exit 0, no conflicted path):11560eb9f(shape-doctor.py and shape_advisory.py: a.gitor an agent file linked into a folder this user cannot search is said once inwhat is hereoragent files,<name>: cannot be read (<reason>), never absent; a.gitoffers no way in (#1110) #1144, adopt-project.py:check's rehearsal and_stored_as_writtentake a parent's-csettings,attr.treetoo, and the source's objects,GIT_ATTR_SOURCEis refused by name before any question, and each throwaway reads its environment first (#1136) #1169 and scaffold-project.py: a partial-clone--pin-sourceshort of the pinned commit or its trees ispin-source-unreadable, named a partial clone withgit fetch --refetch --no-filter <remote> <commit>, and no longer fetched into (#1182) #1223 landed sincebd3e9c148; none touches either file): tree6ddf3d80ec414d64e23f2a431fbb900282d3c3a8.gitmodulesbeside the work tree's and its stage remedies endgit add -- .gitmodules, an index file git cannot parse is refused, and an unmerged one is read as none (#1150) #12270d65ea80c(validate-family.py): tree83ef7600242ba1a4272806b308c839196a1a1e17.gitthis user cannot read is refusedgit-repo-unreadable, exit 2, not a traceback ornot-a-git-repo, and the doctor'smembersrow names it once, never drift (#1148) #11722eabec8e0(validate-family.pyandtest_validate_family_unreadable.py): tree54d0de85bfb8bdb2fe5aff1725b34299f34c429d. That tree, extracted to a scratch repository, rantests/test_validate_family_unreadable.pyas84 passed in 44.23s, so the new row passes beside bootstrap.py, validate-family.py and siblings.py: a root or holder whose.gitthis user cannot read is refusedgit-repo-unreadable, exit 2, not a traceback ornot-a-git-repo, and the doctor'smembersrow names it once, never drift (#1148) #1172's_refuse_an_unreadable_root.id:, a date or arepository:ending in a line feed is refused by name, and a pin'ssource_repository:that is no path orowner/reponame is named as the pin's (#1139) #11665c8afefd4(validate-family.py): treead111a56b117bf83bb72ead4f6ad41472acd3082Closes #1221
Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code