Repository navigation
shape-doctor.py's legs NEXT after #354 does not fit four edge states: a populated mount with no .git but its gitdir kept is sent to validate-pins.py under a sentence saying no update cures it, though the git line does; an unborn HEAD gets the gate, which says pins ok while the row stays red; an empty mount with no .gitmodules section gets a git line that exits 128; and an empty mount whose pin is not 40 hex gets no "run the doctor again" #414
Description
Lane: openreposhape-4 (openRepoShape-4)
Found by this lane's adversarial review of #276 (PR #354, head c30bfb2: its N3, N4, N8 and N9). PR #354's body carries all four as follow-ups at its head cb2a684. #354 is open. Verified at cb2a684. On main 3ae0109 the legs NEXT for all four states is the root's bootstrap line, the line #276 is about. Local bare remotes, an empty global git config, GIT_ALLOW_PROTOCOL=file, and a gh that exits 97 (only the machine row calls it). Not claimed: macOS and Windows, which were not run, and the members row, which takes the same rule (members_verdict) and was not run for these states.
What is wrong. check_legs (shape-doctor.py:1874 at cb2a684) chooses the NEXT by kind:
- checkout (
:1950,:1962-1968): an empty mount, or a HEAD off the pin where the gitlink is the pin. The NEXT is the git line, and a sentence that says what it does. - lockstep (
:1946,:1954,:1958, and theelseat:1969): everything else. The NEXT isvalidate-pins.py, under "no submodule update cures a leg whose gitlink or pin is missing or disagrees, or a mount git cannot read a HEAD in" (lockstep_next,:1779). - When both kinds are present, the checkout wins and "Run the doctor again" is added (
rerun=bool(lockstep),:1985-1986).
Four states do not fit that rule:
U=<openRepoShape checkout> # a clean checkout of PR #354's head cb2a684
# Runs in bash on Linux (GNU coreutils). Nothing leaves the machine: every repository is a local bare one under $R, git
# reads an empty global config, GIT_ALLOW_PROTOCOL=file makes any accidental https/ssh clone fail, and a `gh` that exits
# 97 stands first on PATH.
R=$(mktemp -d); mkdir $R/bin; printf '#!/bin/sh\nexit 97\n' > $R/bin/gh; chmod +x $R/bin/gh; touch $R/gitconfig
export PATH=$R/bin:$PATH GIT_CONFIG_GLOBAL=$R/gitconfig GIT_CONFIG_NOSYSTEM=1 GIT_ALLOW_PROTOCOL=file \
GIT_AUTHOR_NAME=t GIT_AUTHOR_EMAIL=t@t.invalid GIT_COMMITTER_NAME=t GIT_COMMITTER_EMAIL=t@t.invalid
doctor() { python3 $U/shape-doctor.py --root "$1" --json > "$1.json"; }
show() { python3 -c '
import json, sys
r = [r for r in json.load(open(sys.argv[1]))["rows"] if r["id"] == sys.argv[2]][0]
line, _, why = (r["next"] or "").partition(" # ")
print(" " + r["id"] + ":", r["status"], "|", r["reason"])
print(" NEXT:", line); print(" its sentence:", why)
open(sys.argv[1] + ".next", "w").write(line)' "$1" "$2"; }
python3 $U/scaffold-project.py --org testorg --project Atlas --elected-by 'Test Human' --elected-on 2026-10-12 \
--local-remote-dir $R/remotes --work-dir $R/work >/dev/null
pasted() { # paste the NEXT from elsewhere; print the lines of its output that match $2, then its exit
(cd $R && sh -c "$(cat $1.json.next)" > $R/paste.out 2>&1); local rc=$?
grep -E "$2" $R/paste.out | cut -c1-110; echo " pasted: exit $rc"; }
fresh() { git clone -q --recurse-submodules $R/remotes/Atlas.git $1; }
again() { doctor $1; python3 -c 'import json,sys; r=[r for r in json.load(open(sys.argv[1]))["rows"] if r["id"]=="legs"][0]; print(" the doctor again:", r["status"], "|", r["reason"])' $1.json; }
echo "== (1) a populated mount with no .git of its own, its gitdir kept"
C=$R/1; fresh $C; rm $C/spec/.git
doctor $C; show $C.json legs; pasted $C '^(REFUSED|Remediation)'
git -C $C submodule update -q --init --recursive --checkout -- spec; echo " the line its sentence rules out, run instead: exit $?"; again $C
echo "== (2) an unborn HEAD in the leg"
C=$R/2; fresh $C; echo 'ref: refs/heads/never-made' > $C/.git/modules/spec/HEAD
doctor $C; show $C.json legs; pasted $C '^(pins ok|FINDING|REFUSED)'; again $C
echo "== (3) an empty mount whose .gitmodules section is gone"
C=$R/3; fresh $C; git -C $C submodule deinit -q -f spec; git -C $C config -f .gitmodules --remove-section submodule.spec
doctor $C; show $C.json legs; pasted $C '^fatal'
echo "== (4) an empty mount whose pin is not 40 hex"
C=$R/4; fresh $C; git -C $C submodule deinit -q -f spec; sed -i -E 's/^commit: .*/commit: "abc123"/' $C/contracts/spec-pin.yaml
doctor $C; show $C.json legs; pasted $C '^(fatal|error)'; again $COutput at cb2a684:
== (1) a populated mount with no .git of its own, its gitdir kept
legs: FINDING | spec: checked out at 0aef20a5a255, pinned at aa09c2250df4
NEXT: python3 $R/1/scripts/validate-pins.py --root $R/1
its sentence: no submodule update cures a leg whose gitlink or pin is missing or disagrees, or a mount git cannot read a HEAD in. validate-pins.py prints what it finds and names the repair where it has one
REFUSED pin-submodule-uninitialized: spec is not an initialized submodule checkout, so the pinned digest canno
Remediation: run `git submodule update --init --recursive`, then `python3 scripts/validate-pins.py`. If the PI
pasted: exit 2
the line its sentence rules out, run instead: exit 0
the doctor again: ok | 2 leg(s) mounted and checked out at their pins
== (2) an unborn HEAD in the leg
legs: FINDING | spec: populated, but git cannot read a HEAD in it
NEXT: python3 $R/2/scripts/validate-pins.py --root $R/2
its sentence: no submodule update cures a leg whose gitlink or pin is missing or disagrees, or a mount git cannot read a HEAD in. validate-pins.py prints what it finds and names the repair where it has one
pins ok
pasted: exit 0
the doctor again: FINDING | spec: populated, but git cannot read a HEAD in it
== (3) an empty mount whose .gitmodules section is gone
legs: FINDING | spec: the gitlink is there and the directory is empty -- an unbootstrapped clone
NEXT: git -C $R/3 submodule update --init --recursive --checkout -- spec
its sentence: checks each leg out at the commit this repository records for it, and fetches a mount that is empty. Then make bootstrap puts each leg on its tracking branch, and leaves alone one whose branch is already ahead of the pin
fatal: No url found for submodule path 'spec' in .gitmodules
pasted: exit 128
== (4) an empty mount whose pin is not 40 hex
legs: FINDING | spec: the gitlink is there and the directory is empty -- an unbootstrapped clone
NEXT: git -C $R/4 submodule update --init --recursive --checkout -- spec
its sentence: checks each leg out at the commit this repository records for it, and fetches a mount that is empty. Then make bootstrap puts each leg on its tracking branch, and leaves alone one whose branch is already ahead of the pin
pasted: exit 0
the doctor again: FINDING | spec: contracts/spec-pin.yaml carries no 40-hex commit to compare HEAD against
| state | what the row hands over | what pasting it does |
|---|---|---|
(1) populated, no .git in the mount, .git/modules/spec kept |
the gate, under a sentence saying no update cures it | exit 2, pin-submodule-uninitialized, and the gate's own Remediation: says to run git submodule update --init --recursive. That line, run instead, exits 0 and the row is ok. The row's reason quotes the ROOT's HEAD as the leg's (0aef20a5a255 above is the root's), because git walks up out of the mount. |
| (2) an unborn HEAD in the leg | the gate | pins ok, exit 0. The row stays red, and nothing the row names changes it. |
(3) an empty mount whose .gitmodules section is gone |
the git line | fatal: No url found for submodule path 'spec' in .gitmodules, exit 128 |
| (4) an empty mount whose pin is not 40 hex | the git line, and no "Run the doctor again" | exit 0. The re-run is red on the pin, which the sentence did not say was coming. |
Why.
- (1):
check_legsroutes on whether the mount has its own.git, butsubmodule updateroutes on whether the GITDIR exists. With the gitdir kept, the update re-links the mount and cures it, which the sentence rules out. - (2): an unborn HEAD is not a lockstep fact at all. The gitlink and the pin agree, so the gate has nothing to report.
- (3): the checkout kind never asks whether git can find the leg's URL.
- (4):
rerunis computed from OTHER legs only. A leg routed to the checkout kind can carry a lockstep fact of its own (a pin that is not 40 hex), and the empty mount hides it until the next run.
What it should do. For each state, the NEXT either cures the row or is honest that it does not. The decisions are the author's:
- (1) Route a populated mount whose
.git/modules/<path>exists to the git line, or correct the sentence to "unless its gitdir is still there". Separately, read no HEAD for a mount with no.gitof its own, rather than the root's. That is the shape-doctor.py: thelegsrow's NEXT (and themembersrow's) runs<root>/scripts/bootstrap.pywithout asking whether the root has it, so a root that lost it pastespython3: can't open file, exit 2, and for an empty leg mount thelegsNEXT would not cure the row even with the file there (bootstrap.pyonly printsRun git submodule update --init spec) #276 writer's out-of-scope item 1. - (2) No line cures an unborn HEAD without choosing a commit for the person. So either NEXT None with a sentence that names the state, or a checkout of the gitlink in the leg (
git -C <leg> checkout --detach <gitlink>), which moves no ref. - (3) Ask for the URL (
git config -f .gitmodules submodule.<name>.url) before choosing the git line. With none, route to the gate, or to restoring.gitmodulesfrom HEAD when HEAD has the section. - (4) Compute
rerunper leg, so that a checkout-kind leg with its own lockstep fact gets the sentence too.
How to test. Each state in tests/test_doctor_legs_members_next.py, pasted and the doctor run again. Each NEXT either turns the row ok or is None with a sentence that names the state. For (4), the sentence says the run must be repeated.
Verified at PR #354's head cb2a684 (shape-doctor.py sha256 82a5ac78…).
Related: #276 / PR #354, #277 (the sentences move to why; in progress in this lane), #413, #415, and the #276 review's out-of-scope 3 (validate-pins.py reports a leg whose gitdir is gone as pin-commit-unresolvable, a misleading diagnosis).
Activity
brettheap commentedon Oct 14, 2026
CLAIMED — lane openRepoShape-4, session 11c6c0ef-495a-4a05-94cf-811b7b0a1012@Eagle, 2026-10-14T17:16:08Z, for #414
Logged in
lanes/log/openRepoShape-4.mdat42ebffbc1a58683798745fa77228d1db8beadf4d.The three reads (lane-collision-protocol Rule 1):
shape-doctor.py: a mount whose `.gitmodules` section lost only its `u…
shape-doctor.py: the `members` NEXT is the git line and not the holde…
tests/test_doctor_legs_members_next.py: the bootstrap that cures noth…