Repository navigation
validate-pins.py and shape-doctor.py: a leg's git-directory HEAD file at mode 000 passes the gate's stat of HEAD and ends pin-commit-unresolvable, and the doctor reads the leg DRIFTED, where both should name the file this user cannot open #1222
Description
Goal: a leg whose git directory's HEAD file this user cannot open is refused by name, pin-submodule-unreadable, by the gate (validate-pins.py), and shape-doctor.py then reports it in the gate's words, so the gate no longer says the pinned commit is missing and the doctor no longer counts the leg as drift.
Filed by lane openRepoShape-3, from RV1200's independent review of #1200 (#1174) at 5fb38294 (#1200 issuecomment-6362479557), its N2, which reads "a follow-up only if wanted, gate first". It did not hold the landing: RV1200 was READY as a draft, and #1200 is an open draft at 5fb38294, with #1193's branch (l3-1175-unreadable-mount-rows-tested) as its base; that branch's commit f3247e54 is not on main either. RV1200's probe was Python 3.12 and uid 1000, with real modes restored in a finally. Line numbers are main's at ef619c74. The gate's half is on main now. The doctor's git_dir_of exists only at #1200's head.
The gate asks HEAD by stat alone
State.
- The gate's 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 check,
_refuse_an_unreadable_git_dir(templates/assembly-root/scripts/validate-pins.py:443), resolves a leg's git directory (_leg_git_dir,:427) and asks_is_there(gitdir / "HEAD")(:380), which is astat. Astatof a file at mode 000 works while its directory can be searched, so aHEADfile at mode 000 passes._check_pin_digest(:1217) goes on togit rev-parse(:1256), which cannot read HEAD and is refusedpin-commit-unresolvable(:1260), whose detail says the pinned commit is not in that submodule's object store. - The gate already opens a gitfile for the same reason:
_gitfile_opens(:397, 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), "A GITFILE AT MODE 000 STATS".HEADhas no such open. - The doctor at main asks no git directory at all (
shape-doctor.py'smount_read,:3458; that gap is shape-doctor.py: a leg whose git directory this user cannot read (.git/modules/specat mode 000) is counted as off its pin, so the verdict is DRIFTED for a leg at its pin, andplacementcalls it not a git repository #1174). shape-doctor.py: a leg or member whose git directory this user cannot read is named in the gate's words,spec: .git/modules/spec: cannot be read (<reason>), so thelegsandmembersrows never count it as drift,placementnever calls it "not a git repository", and no HEAD is read for it (#1174) #1200 addsgit_dir_of(:3459at its head) and astatof<gitdir>/HEADinmount_read(:3494). RV1200 rules that "git_dir_ofis the gate's_leg_git_dir, and thestatofHEADis the gate's_refuse_an_unreadable_git_dir(validate-pins.py:443)", under a parity test.
What happens. RV1200's N2: "a HEAD file itself at 000 stays DRIFTED at main and here, while the gate says pin-commit-unresolvable. The gate's #968 scope is the same stat of HEAD, so nothing in this PR". At main the doctor reads DRIFTED because it does not ask the git directory; at #1200's head it still does, because its ask of HEAD is the gate's stat. Neither was run; the stat is read in the code above.
Direction. The gate first, then the doctor mirrors the gate. No command and no flag.
-
Gate: in
_refuse_an_unreadable_git_dir, after thestatofHEAD, openHEADwhere it is a regular file, and only then (as_gitfile_opensdoes, since the open of a FIFO waits for a writer), and refusepin-submodule-unreadablenaming the file,<named>: cannot be read (<reason>), with the leg checkout's remedy, as it refuses the git directory today. A git directory with noHEADstays git's, as the docstring says. -
Doctor: once shape-doctor.py: a leg or member whose git directory this user cannot read is named in the gate's words,
spec: .git/modules/spec: cannot be read (<reason>), so thelegsandmembersrows never count it as drift,placementnever calls it "not a git repository", and no HEAD is read for it (#1174) #1200 has landed,mount_read's ask ofHEADbecomes the gate's, so thelegsandmembersrows read INVALID with the gate's line and never DRIFTED, and the parity test gains the same shape. -
Not asked here: git also reads
config,objects,refsandcommondir, which adopt-project.py: a--source, or a plan'ssource.local_path, with a part of its repository git reads at mode 000 (the.git, itsHEAD,objects,refsorconfig, the git directory agitdir:line names, or a directory above it) is refusedsource-unreadablenaming that part, nevergit-failedand never cloned again (#1151) #1186 asks foradopt-project.pyand which, read from_refuse_an_unreadable_git_dir, neither the gate nor the doctor asks. Widening to them is a separate ruling. The same open ofHEADbelongs invalidate-family.py's member check, which has neither of the gate's two checks yet (validate-family.py: a member whose.gitfile or git directory this user cannot read passes_checked_outand is refusedmember-commit-unresolvable, which says the pinned commit is missing, wherevalidate-pins.pynames the unreadable part #1221). -
validate-pins.pyis a pinned copy of the assembly root, so the change moves its bytes and holders see anupstream-changedrow. -
The tests: the gate's rows go in
tests/test_validate_pins_spelled_and_named.py, besidetest_a_leg_git_directory_this_user_cannot_search_is_refused(:835) andtest_a_leg_git_directory_with_no_head_is_left_to_git(:862). The doctor's go intests/test_doctor_unreadable_paths.py, the file shape-doctor.py: a leg or member whose git directory this user cannot read is named in the gate's words,spec: .git/modules/spec: cannot be read (<reason>), so thelegsandmembersrows never count it as drift,placementnever calls it "not a git repository", and no HEAD is read for it (#1174) #1200 extends, with the parity row. -
A gitfile leg whose
.git/modules/<leg>/HEADis at mode 000, and a.git-directory leg whose.git/HEADis at mode 000:validate-pins.pyrefusespin-submodule-unreadablenaming the file, exit 2, and notpin-commit-unresolvable. Each row fails before and passes after. -
A leg whose git directory has no
HEADis still git's answer: the file's existing row passes unchanged. -
After shape-doctor.py: a leg or member whose git directory this user cannot read is named in the gate's words,
spec: .git/modules/spec: cannot be read (<reason>), so thelegsandmembersrows never count it as drift,placementnever calls it "not a git repository", and no HEAD is read for it (#1174) #1200 has landed: the doctor'slegsrow for the same leg is INVALID with the gate's line and not DRIFTED,placementdoes not call it "not a git repository", and the parity test runs theHEAD-at-000 shape against the gate's helper. -
A readable leg (a gitfile, a
.gitdirectory, a member) is unchanged in every row and verdict. -
The mode rows are skipped where a mode cannot be refused (root, Windows), and an in-process stand-in covers those hosts.
-
One mutant, the open dropped, fails the gate row; each mutant run is restored and checked with
cmp.
Related: #1200 (draft), #1193 (its base, for #1175), #1174 (the issue #1200 is for), #968 and #919 (the gate's halves, landed), and #459 (the tracker for setup, scaffold, family and the shared helpers; none of the four trackers names validate-pins.py or shape-doctor.py, so it is the nearest).
Size: M.
Lane: openreposhape-3 (openRepoShape-3)
Activity
brettheap commentedon Oct 23, 2026
CLAIMED — lane openRepoShape-3, session b30e8b49-f249-4668-adb1-1dbfa93c09e1@Eagle, 2026-10-23T18:42:48Z, for #1222
Logged in
lanes/log/openRepoShape-3.mdat7c1ff331bd870d4d3ec63007c7df08c66c8e8d6e.The three reads (lane-collision-protocol Rule 1):