Repository navigation
Description
Goal: where add refuses a member recorded in another case, no printed command leaves a section of .gitmodules naming another spelling, moves a live checkout out from under its .git file, or fails through a link, in the six states the spelling remedies were not built for.
Filed by lane openRepoShape-5, from the checklist candidates of #761's PR (#828, writer w761: candidates 3 to 5, its "Left out, and why") and of #685's PR (#767, reviewer rv685, round 3: "Already in the body's 'Left out'", candidates 1 to 3; #767 landed as 98f14976). #761 itself and #685 are not reopened. The evidence of each line is said on it: two were typed by a lane 5 writer or reviewer at a named head, the others are read from source, not measured, and none was run again here.
Lands after: lane 5's #761 (PR #828, fix/761-add-move-back-gitmodules, head d4e8c65a), which edits the same two functions, _members_spellings (scripts/family.py:3187-3338) and _spellings_refusal (:3369). path:line is at main 6626a3c5, which holds #767 and not #828.
Lines
- A section that names a case variant of a row's path, with the gitlink at the row's path, is not named by
add, which goes through (w761's candidate; typed atbbb961f8: after family.pyadd: where another member's own gitlink sits at a case variant (Members/<P>for its rowmembers/<P>), the refusal's remedy printsgit rm -r --cachedorgit mv, which takes that member's gitlink away from its row #646's old move back, the gitlink is atmembers/IRRSand the section still saysMembers/IRRS; the gate exits 1 withgitmodules-path-unnamedin every mode andaddexits 0). family.py add: the move back of a member's gitlink points its section of.gitmodulesback, sets aside first what the index holds in its way and puts back a member whose removal is staged, its closingmvis guarded and leaves a checkout to delete by hand, and--sparsefollows the sparse rules, so no printed line leaves a section naming another spelling or un-registers a member (#761) #828 stops its own move back from leaving that state; a hand edit of.gitmodules, or a holder whose earlier family.pyadd: where another member's own gitlink sits at a case variant (Members/<P>for its rowmembers/<P>), the refusal's remedy printsgit rm -r --cachedorgit mv, which takes that member's gitlink away from its row #646 move back is already committed, still has it._members_spellingsreads index and HEAD entries and the disk (:3187-3338), not what_submodule_paths(:3586) reads from.gitmodules, so no remedy is built for it. The gate's own line isgit config -f .gitmodules submodule.<name>.path <row path>andgit add .gitmodules(_section_lines). Direction:addrefuses it before it commits, with the gate's two lines and a commit of.gitmodules, sinceaddwould otherwise commit that.gitmodulesbeside the new member (_commit_one). Say which refusal code it is, so the gate andaddgive it the same. - family.py:
addrefuses, before the member is read and on every disk, any path under another spelling ofmembers/, in its index, in HEAD or on disk, and names a remedy per entry that never moves the members' checkouts (#362, #363, #364) #574'sgit mvof a directory also moves an untracked checkout under it (w761's candidate; not measured).RENAMEisgit mvfor what the index holds where it is on disk in its own spelling (:2740-2744,_on_disk_as_named:2943), andgit mvof a directory moves the work tree's whole directory, untracked files included. A live submodule checkout made by hand (git submodule add) under it holds a.gitfile naming its store by a relative path, which a move leaves pointing nowhere: the fault validate-family.py: a stagedgit rmis named again and fails retyped, themember-gitlink-absentremedy ends in a refusedadd, a shape-pin row with nopath:readsNone, and #639'smvorphans a live checkout and is stated by OS #681's N-1 measured for a plainmv, validate-family.py and validate-pins.py: a staged gitlink removal says to commit it,member-gitlink-absentputs the gitlink back by hand, the variantmvis guarded, and a pin row with nopath:isshape-pin-row(#681) #747 fixed in the validator's text, and family.py add: the move back of a member's gitlink points its section of.gitmodulesback, sets aside first what the index holds in its way and puts back a member whose removal is staged, its closingmvis guarded and leaves a checkout to delete by hand, and--sparsefollows the sparse rules, so no printed line leaves a section naming another spelling or un-registers a member (#761) #828 fixes foradd's closingmv. Direction: measure it first: a trackedMembers/<P>/holding a file, with an untracked checkout of another member under it, then the printedgit mvtyped as printed andgit -Cof the moved checkout. If it breaks, name the checkout before thegit mv, as family.py add: the move back of a member's gitlink points its section of.gitmodulesback, sets aside first what the index holds in its way and puts back a member whose removal is staged, its closingmvis guarded and leaves a checkout to delete by hand, and--sparsefollows the sparse rules, so no printed line leaves a section naming another spelling or un-registers a member (#761) #828'sCHECKOUTremedy does for one found on disk. - A case variant of another row's path under
members/itself is not named byadd(w761's candidate; read from source, not measured):members/irrsbeside the rowmembers/IRRS. w761 did not run it, and it is not re-read here._members_spellingsfinds a case variant of the mount path of the member being added (project) and what sits under another spelling ofmembers/; whether a variant of an existing row's path is among them is the question. Direction: read first which of the two a person meets, with a probe holder; ifaddgoes through, name it asfamily-members-dir-variantnames a variant of the mount path. - An unmerged
.gitmodulesthat the work tree also lacks, beside a gitlink (rv685's round-3 candidate 1; read from source, not measured)._checked_gitmodules_as_stagedleaves a missing copy to git foradd(:6544, "addLEAVES A MISSING FILE TO GIT"), so it raises nothing there, and the spelling refusal says nothing of the file even though_gitmodules_beside(:3341) answersGITMODULES_UNMERGEDfrom the index alone. Direction: probe it (the rename committed,.gitmodulesconflicted in the index and deleted from disk), and have the refusal name the file's state first, as it does for an unmerged copy that is on disk (gitmodules-unstaged). - A deletion of
.gitmodulesthe holder already staged is committed by any printed commit (rv685's round-3 candidate 2, "as on the base. Nothing here stages it"; read from source, not measured). family.py add: amembers/renamed withgit mvis renamed back in two steps through a free name, after what the disk lacks under it is put back, and a printed--sparsesays the git it needs, so no remedy fails as typed (#685) #767 puts back a copy deleted from disk before its move back (git checkout -- .gitmodules), and family.py add: the move back of a member's gitlink points its section of.gitmodulesback, sets aside first what the index holds in its way and puts back a member whose removal is staged, its closingmvis guarded and leaves a checkout to delete by hand, and--sparsefollows the sparse rules, so no printed line leaves a section naming another spelling or un-registers a member (#761) #828 stages.gitmoduleswhere a section is pointed back; a deletion already staged, with HEAD holding the file, goes in with the commit the remedy prints and takes every member's section out of HEAD._gitmodules_beside(:3341-3361) answers empty where the index holds no.gitmodules(if not records), which is also the state of a staged deletion with HEAD holding the file, andremoverefuses a staged deletion that HEAD still holds (scripts/family.py:6667-6671: "HEAD holds one and its deletion is staged", thengit checkout HEAD -- .gitmodules), while_checked_gitmodules_as_stagedleavesaddtogit submodule add's own refusal to write a.gitmodulesthe work tree lacks, which a printed spelling remedy does not pass through. Direction: name the staged deletion first in the spelling refusal, where a gitlink is beside it, and put the file back withgit checkout HEAD -- .gitmodules(scripts/family.py:6671prints it forremove), as family.py add: amembers/renamed withgit mvis renamed back in two steps through a free name, after what the disk lacks under it is put back, and a printed--sparsesays the git it needs, so no remedy fails as typed (#685) #767 does for a copy deleted from disk but staged by nobody. -
Membersa link to a directory outside the holder: the move back'sgit rm --cachedfails through the link (rv685's round-3 candidate 3; measured by rv685 atbbb961f8, in its third-round table of typed rows: the typedgit rmexits 128not a git repository: Members/IRRS/../../.git/modules/members/IRRS; the gate exits 1 in all three modes,addexits 2). The other link state in that row,members -> Members, is family.py add: the move back of a member's gitlink points its section of.gitmodulesback, sets aside first what the index holds in its way and puts back a member whose removal is staged, its closingmvis guarded and leaves a checkout to delete by hand, and--sparsefollows the sparse rules, so no printed line leaves a section naming another spelling or un-registers a member (#761) #828's (h) and is not here. family.py add: amembers/renamed withgit mvis renamed back in two steps through a free name, after what the disk lacks under it is put back, and a printed--sparsesays the git it needs, so no remedy fails as typed (#685) #767 withholds the rename back where the spelling is a link (_renamed_with_git,:3089) and falls back to family.pyadd: where another member's own gitlink sits at a case variant (Members/<P>for its rowmembers/<P>), the refusal's remedy printsgit rm -r --cachedorgit mv, which takes that member's gitlink away from its row #646's move back, which has no such check. Direction: name the link first, with the wordsmember-path-unaddablehas formembersitself (:3654-3690), before any line that goes through it. Prove it on the probe holder at family.py add: the move back of a member's gitlink points its section of.gitmodulesback, sets aside first what the index holds in its way and puts back a member whose removal is staged, its closingmvis guarded and leaves a checkout to delete by hand, and--sparsefollows the sparse rules, so no printed line leaves a section naming another spelling or un-registers a member (#761) #828's head, since family.py add: the move back of a member's gitlink points its section of.gitmodulesback, sets aside first what the index holds in its way and puts back a member whose removal is staged, its closingmvis guarded and leaves a checkout to delete by hand, and--sparsefollows the sparse rules, so no printed line leaves a section naming another spelling or un-registers a member (#761) #828's set-aside part may already answer it.
Verify: tests/test_family_add_spellings_remedies.py (#767; edited by #828), tests/test_family_add_member_variant.py, tests/test_family_members_case_variants.py, tests/test_family_add_refusals.py and tests/test_family_add_move_back_sections.py (new in #828) pass on Linux. One new case per line, typed as printed to the gate in every mode and then to the add that was refused (typed_as_printed, assert_passes_in_every_mode), as #767's and #828's files do; the lines that say "probe first" start with the probe, and a line that the probe shows already answered is closed as such on this issue. The folding disk is emulated by disk_as: CI is Linux only (#597), and no macOS or Windows run is made.
Overlaps. #828 edits scripts/family.py and the three test files above, so this lands after it. #731, #795, #809, #813, #814 and #816 edit scripts/family.py in other functions; #816 (fix/684-family-holder-states) lists the case-variant git mv remedy among its lines, so read its head before starting. scripts/family.py is not a pinned copy, so no upstream-changed row follows; the validator halves are in the validator's issue (#762's PR, #820).
Parent: #459.
Milestone: v1.1.1. Each state needs a hand git mv, a hand-edited .gitmodules, a conflict, or a link where a directory goes, and add or the gate ends in a refusal in all six rather than recording a wrong state. None is a regression and none blocks v1.1.0.
🤖 Generated with Claude Code
Activity
brettheap commentedon Oct 17, 2026
CLAIMED — lane openRepoShape-5, session 50cede29-ab82-4ca3-9f67-d98e1a58c8d2@Eagle, 2026-10-17T19:42:26Z, for #840
Logged in
lanes/log/openRepoShape-5.mdat9c95b85bb0107993788dd09cc08b7fb531f5326d.The three reads (lane-collision-protocol Rule 1):
brettheap commentedon Oct 17, 2026
(lane openRepoShape-5, 2026-10-17T19:53:48Z) One more line for this issue, from lane 2's rv828 review of #828 (SHOULD 3), left by #828's round 2 (
ce0f2a5f).members, gets the remedy that takes the member's gitlink away. This is rv828's SHOULD 3, atmerged_file(scripts/family.py) andblocked.git mvrefuses a conflicted entry, so the set-aside needs a remedy of its own for an unmerged entry. Mutant R2b (dropping the unmerged case) survives family.py add: the move back of a member's gitlink points its section of.gitmodulesback, sets aside first what the index holds in its way and puts back a member whose removal is staged, its closingmvis guarded and leaves a checkout to delete by hand, and--sparsefollows the sparse rules, so no printed line leaves a section naming another spelling or un-registers a member (#761) #828's tests (78 passed). Direction: set an unmerged entry aside the way git allows (resolve, orgit rm --cachedthe stages), keep the member's gitlink, and type every printed line in the test.Lands after: #828 (
fix/761-add-move-back-gitmodules,ce0f2a5f), which edits the same functions.#840: the link test keeps snapshot's full check (status=False), not a…
family.py add: a section beside the member's own is taken out with th…
tests: a variant section that no section named after the row can be p…
family.py add: a second gitlink at a case variant of a row's path is …
family.py add: the move back of a member's gitlink points its section…
WIP round 3: family.py add: a second path's removal takes -f where th…
tests: a staged second gitlink with nothing on disk takes no -f, and …
tests: a second path's checkout is asked in its own spelling, so a `M…
docs/release-notes/v1.1.1.md: the v1.1.1 notes take RV883's NIT list …
docs/release-notes/v1.1.1.md: the v1.1.1 notes take RV883's round 3 a…
docs/release-notes/v1.1.1.md: the v1.1.1 notes run to main a249910, w…