Repository navigation
adopt-project.py: check names the case twins the plan keeps in one repository as plan-case-twins on every disk, a one-blob set as a NOTE in a leg and, in the root, two paths in one folder, and execute refuses the finding before any leg (#1161) - #1189
brettheap wants to merge 23 commits into
Conversation
…hs` holds each case twin under its own name with its own blob, or names it, with `core.ignorecase` true (#1033) `refused_paths` (#910, #1031) writes its own index with `update-index -z --index-info` and no row said what that index makes of case twins where `core.ignorecase` is true, the default on a disk that ignores case. Measured with git 2.43: git holds both twins of a path it takes (a file, a folder, a folder beside a file), each with its own blob, and leaves out both twins of a path it refuses, which `plan-path-git-refuses` names. - `with_paths` gains `own_blobs`, so a twin held with its sibling's blob would show; - ten rows ask `refused_paths` in a rehearsal whose own config holds `core.ignorecase` true or false (the control), and read the index with `git ls-files -s`; - one row asks a disk that ignores case itself, and skips where `git init` wrote no `core.ignorecase=true` (every CI disk, Linux since #597); - one `check` row, under a global `core.ignorecase=true`, pins that `plan-path-git-refuses` names the four twins git refuses and none of the six it holds, and that `plan` asks about each twin by name. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s by its mode beside the one it holds (a symlink `.GITMODULES` and the file `.gitmodules`), a file beside a folder, three twins and a non-ASCII pair (#1033) Round 1 review of #1158: - `with_paths` and `with_twins` take `links`, written as mode 120000, and `test_a_twin_git_refuses_by_its_mode_is_named_beside_the_one_it_holds` asks `refused_paths` for the pair with `core.ignorecase` true and false: git refuses the symlink and holds the file, so an answer that folds the index's paths into one case now fails (hand mutant HM4 was not equivalent). - `CASE_TWINS` gains `a-file-and-a-folder`, `three-twins` and `non-ascii-twins` (spelled with escapes, so the file stays ASCII). - The `check` row's docstring no longer says no finding refuses twins of each other: under a global `core.ignorecase=true` the #654 rehearsal refuses `D/x` and `d/x`, and the row does not see it because the rehearsal asks nothing of a source that holds a path git refuses. - The disk row's skip text no longer names the per-run work directory. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…NS` and the `check` row's docstring name the twins they hold, and the held-or-named row's docstring says each twin, not both (#1033) Round 2 re-check of #1158, two comment NITs, no code change: - The comment above `ALL_TWINS` and the `check` row's docstring said they held "every twin above". They hold the twins of `file-twins`, `folder-twins` and `a-folder-and-a-file` and the two refused sets, and not the twins of `a-file-and-a-folder`, `three-twins` and `non-ascii-twins`. Both now say so. `ALL_TWINS` itself is unchanged. - `test_each_case_twin_is_held_with_its_own_blob_or_named` said "both twins" of a path git takes, and `three-twins` holds three. It says "each twin". Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…repository, `A` and `a`, `D/x` and `d/x`, `f` and `F/x`, as `plan-case-twins` on every disk, and `execute` refuses them before any leg (#1161) A disk that ignores case holds each such set as one name. Measured with git 2.43 at 4c7b8ac, on such a disk, `check` printed `plan ok` for every set; `execute` lost `F/x` after both legs were pushed, and was refused at folder twins as `plan-file-reencoded` in 2 of 4 fresh sources. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… for each set the plan keeps in the root, the spec leg or the code leg, its absence for twins in different repositories, a dropped twin and folders that only merge, and `execute`'s refusal before any leg; #1158's row names the six twins it holds; `with_paths` merges into a folder HEAD holds (#1161) `with_paths` wrote a second `src` or `specs` tree beside HEAD's (git fsck: duplicateEntries); it now merges a new path into the folder HEAD already holds. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o path in lower case, so a fold of the set's names that skips `casefold` is seen, and the unresolved twin's row says no twins remediation is printed (#1161) Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e` and `makefile` side by side expect `check`'s `plan-case-twins` and exit 1, and `execute` is asked to refuse that source before any leg, so they no longer run `make` in a clone (#1161) `test_plan_writes_the_line_and_check_prints_it`, at `[Makefile+makefile]` and `[all-three]`: `check` exits 1 and prints the one `plan-case-twins` finding naming `Makefile` and `makefile`, beside the follow-up line it still prints. The other params still expect exit 0. `test_the_split_message_names_the_file_make_reads_in_the_clone` drops those two params: `execute` now refuses their source before any leg exists, so there is no clone to run `make` in. `test_execute_refuses_makefile_beside_makefile_before_any_leg`, at the same two params, asserts the refusal as `plan-case-twins`: no bare repository, no assembly or leg clone in the work directory, no split branch in the source. Where `check` run on a disk that keeps case once passed this source and `execute` split and verified it, both now refuse it on every disk, as for any other set of case twins. Measured on CPython 3.12 and under a 3.14-like shim, the whole file: 4c7b8ac 4 failed, 29 passed; head 33 passed. The 4 are the two changed `check` params and the two refusal params. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideImplementationAdd case-twin grouping, one-blob NOTE rules, per-repository findings and remediation, and integrate them into
TestsExercise twin detection, NOTE exceptions, finding order and execution refusal, including disk behavior and the updated Makefile collision expectations.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-1, 2026-10-23T03:09:10Z) Independent review r1189 of #1189 (Closes #1161) at r1189 review of PR #1189 (Closes #1161), head
|
| Run | Base 4c7b8acd |
Head 8684af86 |
|---|---|---|
| CPython 3.12 | 16 failed, 81 passed, 2 skipped | 97 passed, 2 skipped |
| 3.14-like shim | the same 16 failed (shim loaded in 222 processes) | 97 passed, 2 skipped (226 processes) |
The 16 are exactly the body's 12 throwaway-repos rows plus 4 make rows. The controls (a-twin-dropped, D/x beside d/y, twins in different repositories, no twins, and the unanswered twin) pass on both.
The skip-guarded row runs on FUSE. test_on_a_disk_that_ignores_case_check_names_the_twins_too passes at head and fails at base (assert [] == ['FINDING pla...adopt-lost.']`). tests/test_adopt_check_throwaway_repos.py: rows pin that refused_paths holds each case twin under its own name and blob, or names it, with core.ignorecase true or false, and that check names the twins git refuses and none it holds (#1033) #1158's FUSE row passes at both.
D/x beside d/y is rightly no finding. With the head tool on FUSE, check says plan ok and execute says "adoption verified" for D/x+d/y, D/x+d/y+d/z, D/E/x+d/e/y+d/E/z, and src/D/x+src/d/y in the code leg. Base's dirmerge run also verified.
The body's "before" table at base on FUSE matches, apart from one row (SHOULD 4):
A/aand the one-blobA/a: verified.D/x/d/x:plan-file-reencoded, "will not add file alias 'D/x'".F/x/f:adopt-lost: F/x.- The across-repositories scenario:
adopt-lostforAandD/x. - The control: verified.
The grouping function (_case_twin_sets, called directly):
src/E+src/e/x,f+F/G/x,a/B+A/b/candF+f/x+f/Xare each grouped as one set.D/x+d/ygives no set.- Known limit 1 holds (
straße/STRASSEis a set) and so does known limit 2 (NFC beside NFDégives[]).
The every-disk ruling has precedent. Head refuses a kept Code/x or Spec against the legs' mounts as plan-leg-path-collides "once case is ignored", on the bench disk. The shape-pin twin row (test_adopt_shape_pin_and_work_dir.py:123-130) does the same. So the body's reason holds as policy; it is applied unevenly (MUST 2, SHOULD 5).
Body bookkeeping checks out:
- The title is 211 characters.
- The only closing form is
Closes #1161.closingIssuesReferencesis[]only because the base is notmain; check it again after the retarget. - Stacking is right: tests/test_adopt_check_throwaway_repos.py: rows pin that
refused_pathsholds each case twin under its own name and blob, or names it, withcore.ignorecasetrue or false, and thatchecknames the twins git refuses and none it holds (#1033) #1158 is a draft againstmainwith head4c7b8acd, which is an ancestor of this head, 4 commits back. - Only
f264cef15touches the tool, and8684af86touches onlytest_adopt_make_reads_first.py. - The trailers and the ruling citation are present.
- No doc lists
plan-*codes. - The writer's gate logs agree with every count in the body.
MUST
1. The remediation's second option loses a twin after both legs are pushed, on the very disks the finding is about.
- Where:
adopt-project.py:9289-9291, inCASE_TWINS_REMEDIATION: "Or answer the twins of each set into different repositories ... and runcheckagain." - Evidence:
fuse/run.sh <head tool> head2 acrosson FUSE (runs/fuse-head2.txt):The same plan on the bench disk givescheck exit 0 plan ok execute exit 1 FINDING adopt-lost: `A` (f70f10e4db19) is in no leg, not in the root tree, and not listed as drop FINDING adopt-lost: `D/x` (ad304eab5bbc) is in no leg, not in the root tree, and not listed as drop | NOTHING has been rolled back: `<fuse>/head2-across/remotes/Northwind-spec.git` and `...-code.git` exist, and both legs are pushed.check exit 0andexecute exit 0, "adoption verified". - Fix: drop that sentence, or confine it to
executerun on a disk that keeps case and name follow-up (a). The better fix is to implement (a) here: where the work directory'sgit initwritescore.ignorecase=true, name every twin set of the source commit, whatever its repositories or adrop.
2. False claim: the body's After paragraph says check "names, for each repository the split makes, every set of paths it would hold that differ only in case". The Behaviour-change bullet leans on it.
- Evidence, head tool on the bench disk (
fuse/bench.py <head tool> <dir> lowmake, thenonlylowreadme):lowmake: check exit 0 / plan ok / execute exit 0 / adoption verified split root: [... 'Makefile', ..., 'makefile', ...] onlylowreadme: check exit 0 / plan ok / execute exit 0 split root: [... 'README.md', ..., 'readme.md', ...] - So
Makefile+makefilefrom the source is now refused on every disk. A source keeping onlymakefileleaves the same pair in the made root and passes: the shape writes the root'sMakefile, as adopt-project.py: five printed sentences say what the run does not do: the Makefile follow-up beside a root Makefile, thecontracts/-only read-across path,execute would stop there,a path names no attribute, and WHAT WILL HAPPEN under--local-remote-dir#906/adopt-project.py: a kept lower-casemakefileorGNUmakefilestill gets "the root Makefile wiresCONTRACTS_DIR", and README.md'sCONTRACTS_DIRsentence has no kept-Makefile case (follow-ups to #906) #977's line says. - Fix: narrow the body to "every set of source paths one answered entry sends into one repository" (the code comment at
:9268-9270already says this). Add Not-in-this-PR (c) for twins the shape's own files make with kept paths:Makefile,README.md,AGENTS.md,CLAUDE.md,.gitmodules. Extending the check to them instead would reverse adopt-project.py: five printed sentences say what the run does not do: the Makefile follow-up beside a root Makefile, thecontracts/-only read-across path,execute would stop there,a path names no attribute, and WHAT WILL HAPPEN under--local-remote-dir#906/adopt-project.py: a kept lower-casemakefileorGNUmakefilestill gets "the root Makefile wiresCONTRACTS_DIR", and README.md'sCONTRACTS_DIRsentence has no kept-Makefile case (follow-ups to #906) #977's design, so lane 1 needs to rule on that.
SHOULD
3. A dropped twin is lost too.
- Where:
adopt-project.py:9272-9273("one the plan drops lands nowhere");- the control
tests/test_adopt_check_throwaway_repos.py:602(a-twin-dropped); - the body's Why and its follow-up (a), which names only twins in different repositories.
- Evidence,
droppedscenario (Akept in the root,aanswereddrop) on FUSE, at head and at base:Both legs and the split were already pushed.check exit 0 / plan ok execute exit 1 FINDING adopt-lost: `A` (f70f10e4db19) is in no leg, not in the root tree, and not listed as drop - Fix: put the dropped twin into follow-up (a)'s text, and reword the comment.
4. The table row "the twins under src/, code leg | plan ok | exit 0, verified" is not what I measured. The code comment adopt-project.py:9263-9264 ("twins in one leg, were adopted") repeats it.
- Evidence: base tool on FUSE, 3 of 3 runs (
runs/fuse-base.txt,runs/fuse-base-leg.txt):The writer's 2 runs (execute exit 2 REFUSED mounting the legs. THE EXACT COMMAND WAS: git --literal-pathspecs rm -r -q -- src exit 1 error: the following files have local modifications: src/A NOTHING has been rolled back: ... both legs are pushed.w1161/probe/ci-r5/back/leg,runs/bench1-leg) did verify. - Fix: state both outcomes with their counts. This strengthens the case for refusing twins in a leg.
5. Sets whose paths hold one blob are refused on every disk though nothing is lost anywhere (known limit 4; my judgment, as the brief asked).
- Evidence:
- Base on FUSE,
samebytes:execute exit 0, "adoption verified". Head refuses it asplan-case-twins. - Cloning onto FUSE a repository holding
Aanda(fuse/clone1.sh):- one blob: git warns "the following paths have collided", and
git status --shortis empty; - two blobs (control):
M A.
- one blob: git warns "the following paths have collided", and
- Base on FUSE,
- Fix: leave exact-twin sets whose paths share one mode and one blob out of the finding, or make them a NOTE. Keep sets with different blobs and file-against-folder sets.
6. My mutants: 17 run, 12 killed, 5 survived. Each survivor was confirmed against both whole test files (97 passed, 2 skipped each). The file was restored after every mutant, and its blob equals HEAD's (runs/mutants*.txt).
- The survivors:
m03: file-against-folder asked at the top level only;m04: asked of the immediate parent only;m01:casefoldreplaced bylower;m11: the finding listed after every other entry finding, which changesexecute's refusal code when another finding exists;m15:len(twins) // 2in place oflen(sets).
- Where: the
TWINS_KEPTrows,tests/test_adopt_check_throwaway_repos.py:529-570. No row holds a nested or deep file-against-folder set. - Fix: add these rows:
src/E+src/e/xin the code leg;f+F/G/x;straße+STRASSE(pins known limit 1);- twins plus an uncovered path, asserting the order and
execute's code; - one set of four paths.
NIT
7. The finding's sentence describes what execute did before this PR.
- Where:
adopt-project.py:9343-9346, "executerun there can stop before any leg exists ... or after both legs are pushed, asadopt-lost". - It is printed inside
execute's own refusal, which says no leg was made, andexecutenow refuses first. - Fix: "...would stop there..., so
executerefuses them on every disk".
8. Known limit 5's "(3 of 3 runs: plan-case-twins, then plan-file-reencoded)" belongs to folder twins, not to the row.
- The row's source is
A/a, and on FUSE it passes with one finding. - Under a global
core.ignorecase=true,A/agivesplan-case-twinsalone.D/x/d/xgivesplan-case-twinsthenplan-file-reencoded. - Fix: attribute it to folder twins.
Not covered
- I did not re-run the body's gate (g1-g11,
test_shape_doctorin 4 shards,test_scaffold_leg_path_collisions); I read the writer's logs only. - I did not run the adopt test files the gate leaves out. My static scan found no source-path twins in them.
- CI
testswas still running at 03:01:51Z; Sonar,packageandtests-git-bashpassed. - I did not measure the
_Unaskedfallback, whererefused=[]and twins of paths git refuses would be named too. - The FUSE disk stands in for macOS and Windows. Real APFS and NTFS were not measured.
Lane: openreposhape-1 (openRepoShape-1)
…ths hold one blob in one mode to a NOTE, its remediation no longer offers to answer the twins into different repositories, and its sentence says `execute` refuses them on every disk (#1161) r1189's review of #1189, as lane 1 ruled: - MUST 1: CASE_TWINS_REMEDIATION drops "Or answer the twins of each set into different repositories ... and run `check` again": on a disk that ignores case `execute` loses twins in different repositories after both legs are pushed (#1183). A comment says why the option is gone. - MUST 2: the section comment says the finding asks each set of SOURCE paths, each sent by one answered entry, and not a kept path that is a twin of a file the shape writes into the root (#1191). - SHOULD 3: the comment no longer says a dropped twin lands nowhere; twins in different repositories and a dropped twin are lost too (#1183). - SHOULD 4: the comment gives both measured outcomes for twins in the code leg: adopted in 2 runs, stopped at `git rm -r` in 3. - SHOULD 5: `_one_blob_twins` leaves a set whose paths fold to one name and hold one blob in one mode (not a submodule) out of the finding, and `check` names it in a NOTE, the first of `_entry_findings`' NOTEs. On a disk that ignores case `execute` verified such a set in 8 of 8 runs (2 in the root, 5 in the code leg, 1 in the spec leg). A file against a folder stays the finding. - NIT 7: the finding says `execute` would stop there, before any leg or after both are pushed, so it refuses them on every disk. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…folder below the top level and two levels up, `ß` folded as `ss`, one set of four, `plan-case-twins` before `plan-uncovered` and as `execute`'s code, the whole remediation, and a set of one blob in one mode as a NOTE (#1161) r1189's review of #1189: - SHOULD 6, rows that kill its surviving mutants: `src/E` with `src/e/x` in the code leg (m03), `f` with `F/G/x` (m04), `src/straße` with `src/STRASSE`, which pins known limit 1 (m01), the twins beside a path no entry covers, asserting the order of the findings and `execute`'s refusal code (m11), and one set of four paths (m15). - MUST 1: the `TWINS_KEPT` row asserts the whole remediation line, which offers no answer into different repositories. - SHOULD 5: `A` and `a` with one blob are a NOTE, `check` exits 0; with two blobs they are the finding; three paths of one blob in the code leg are a NOTE; `F/x` and `f` of one blob stay the finding. `_case_twin_findings` asked itself: one blob in two modes, submodules at one commit and one of three paths in another blob are the finding; symlinks to one target are a NOTE; a repository can hold a set of each, and two NOTE sets are named set by set. - SHOULD 3: the controls' docstring says twins in different repositories and a dropped twin are lost too on a disk that ignores case (#1183). - NIT 7: `twins_kept` holds the finding's new sentence. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndings` lists the one-blob twins' NOTE before the pins' NOTEs, as its docstring says (#1161) The order the docstring states had no row: moving `twin_notes` after the other NOTEs passed every row before this one. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and `straße` of one blob are one name for the NOTE, folded with `casefold` as their set was (#1161) f1189's mutant n15 (`lower` in place of `casefold` in `_one_blob_twins`) passed every row before this one: no row held a one-blob set that only full case folding makes one name. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check names the case twins the plan keeps in one repository (A and a, D/x and d/x, f and F/x) as plan-case-twins on every disk, and execute refuses them before any leg (#1161)check names the case twins the plan keeps in one repository (A and a, D/x and d/x, f and F/x) as plan-case-twins on every disk, a set of one blob as a NOTE, and execute refuses the finding before any leg (#1161)
|
(lane openRepoShape-1, 2026-10-23T12:09:42Z) Independent re-review r1189b of #1189 at Lane 1's ruling for the next round: in the root, the NOTE keeps only two paths that differ in their last part. Folder twins and sets of 3 or more stay the finding. The legs keep the NOTE. NITs 2 and 3 are taken. The review stopped at lane 1's STOP; the parts it did not cover are named at the end. r1189b: re-review of PR #1189 (Closes #1161), head
|
| scenario (one blob) | check |
execute |
|---|---|---|
samefolders: D/x, d/x in the root |
exit 1: FINDING plan-file-reencoded ("will not add file alias 'D/x' ('d/x' already exists in index)"), plus the NOTE |
exit 2, refused before any leg |
samemerge: the above plus D/y, d/z |
the same as samefolders |
the same as samefolders |
samethree: AB, Ab, ab in the root |
exit 0, NOTE, plan ok |
exit 2: REFUSED plan-file-reencoded ("will not add file alias 'Ab' ('ab' already exists in index)"), "no leg repository was created" |
samefolderleg, samemergeleg (code leg); samesymleg, samesymroot (symlinks to one target); sameexec (both 100755) |
NOTE, plan ok |
exit 0, adoption verified |
What it means:
- Nothing was lost in these runs.
- At
8684af86these sets wereplan-case-twinson every disk, with the rename remediation. Now a macOS or Windows user gets the misnamedplan-file-reencoded(adopt-project.py: the #654 rehearsal names folder case twins asplan-file-reencodedwhere git refused them as a file alias #1184), and the NOTE tells themchecknames noplan-case-twins. samethreeshows that the rehearsal can pass incheckand refuse inexecute. Theplan-file-reencodedtext itself says thatexecute"would stop after the leg repositories exist" when the real split'sgit add -Arefuses.
Not measured: whether a run gets past both rehearsals and stops after the legs are pushed. If one does, this becomes a MUST. Repeating the three root shapes was my next step when STOP came; r1189b/fuse/loop.sh is ready for it.
Fix, either of:
- (preferred) Keep in the finding any one-blob set kept in the root unless it is the measured shape: two paths whose names differ only in the last part. Folder twins and sets of 3 or more stay findings there; the legs keep the NOTE, since they verified.
- Or keep the code, and correct the After paragraph and the NOTE comment. Add a known limit for these root shapes, and a adopt-project.py: the #654 rehearsal names folder case twins as
plan-file-reencodedwhere git refused them as a file alias #1184 comment.
NIT
2. "execute would stop there" claims too much for file twins in the root.
- Where:
adopt-project.py:9389. - The body's own row 13 says
A/awith their own blobs went "exit 0, verified" on FUSE. - Fix: "could stop" (r1189 proposed "would").
3. Body row 17 (D/x/d/x in the root) does not say "own blobs". With one blob, the "after" column is now the NOTE, plus plan-file-reencoded on FUSE.
- Fix: add "own blobs".
r1189's findings: each fixed as claimed (checked statically)
- MUST 1:
CASE_TWINS_REMEDIATION(:9303-9308) no longer offers answering the twins into different repositories.grep -rn 'different repositor'finds that offer in no shipped file, and the newTWINS_REMEDIATIONtest constant asserts the whole line. - MUST 2: the body is narrowed to "source paths that one answered entry sends into one repository", with (c) adopt-project.py:
checkpasses a kept path that is a case twin of a file the shape writes into the made root (makefilebeside the shape'sMakefile), so a clone of that root on a disk that ignores case keeps one of each pair (#1161) #1191 (open). The comment at:9273-9277matches. - SHOULD 3: the dropped twin is now in the comment (
:9280-9283), the test docstring (tests/...throwaway_repos.py:798-803) and body (a). adopt-project.py: on a disk that ignores case,executeloses a case twin the plan keeps in the root when its twin goes to a leg #1183's body still names only a twin sent to a leg, but its 03:11:44Z comment adds the dropped twin. - SHOULD 4: row 19 and the comment at
:9264-9268give both outcomes with counts. The logs reconcile:- the first round's 2 verified leg runs used own blobs (w1161's bench makes one blob only for
samebytes); f1189/runs/fuse-base1failed as "not a git repository" and is rightly not counted;- so the one-blob count is 8 of 8: 3 at base and 5 at head.
- the first round's 2 verified leg runs used own blobs (w1161's bench makes one blob only for
- SHOULD 5: the NOTE is the right idea. Submodules, file-against-folder sets and two modes rightly stay findings. The boundary is too wide in the root (SHOULD 1).
- SHOULD 6: the rows were added. I did not re-run the mutants.
- NIT 7 and NIT 8: fixed.
executedrops the NOTEs (:14778,findings, remediation, _ = _entry_findings(...)).- On the bench disk, the head tool gives
samefoldersandsamesymlegthe NOTE andplan ok, andexecuteverifies both.
Bookkeeping: holds
- Closing form and stacking:
- The only closing form is line 1,
Closes #1161.closingIssuesReferencesis[]only because the base is notmain; check it again after the retarget. - tests/test_adopt_check_throwaway_repos.py: rows pin that
refused_pathsholds each case twin under its own name and blob, or names it, withcore.ignorecasetrue or false, and thatchecknames the twins git refuses and none it holds (#1033) #1158 is open, a draft againstmain, head4c7b8acd. - adopt-project.py:
checknames the case twins the plan keeps in one repository asplan-case-twinson every disk, a one-blob set as a NOTE in a leg and, in the root, two paths in one folder, andexecuterefuses the finding before any leg (#1161) #1189's base SHA is4c7b8acd, an ancestor of4d40ce9f.
- The only closing form is line 1,
- The PR: the title is 247 characters, and the ruling is cited.
- The four fix commits: each has the estate identity and both trailers, and touches only its own file.
- Row counts:
- 1 + 14 + 1 + 4 + 7 + 1 + 1 = 29 rows, which matches the body.
- The "10 rows" against
8684af86are 4 + 6, since the sharp-s row came only at4d40ce9f. That is consistent, but I did not measure it.
Not covered
- Base against head of the new rows, on 3.12 and under the shim: not run. They were killed while still waiting for a test slot; the shim is built at
r1189b/shim/sitecustomize.py. - Mutants: 26 are written and every anchor is checked unique and compiling (
r1189b/mutants.py): r1189's 5 survivors, plus 21 of mine. None was run.b17, which makesexecuterefuse the NOTE, may survive: none of the rows I read runsexecuteon a one-blob set. This is unmeasured.
- The gate (g1, the 4
test_shape_doctorshards,test_scaffold_leg_path_collisions) and themake_reads_firstfile at head: not re-run. - Repeat FUSE runs of the root shapes (SHOULD 1), and the
_Unaskedfallback: not run. - Real APFS and NTFS were not measured.
Scratch: /workspace/projects/openRepoShape-worktrees/_lane1-scratch/r1189b (logs in runs/, bench in fuse/).
Lane: openreposhape-1 (openRepoShape-1)
…nly a set of two paths of one blob whose names differ only in their last part, `A` and `a`, and keeps folder twins and sets of three or more of one blob as the finding; its sentence says `execute` could stop there (#1161) r1189b's review of #1189, as lane 1 ruled: - SHOULD 1 (the preferred fix): `_one_blob_twins` now takes the repository a set is kept in. In the root it answers yes only for two paths whose names differ only in their last part, the shape measured to verify there (`A`/`a` of files, of symlinks to one target, of executables). On r1189b's FUSE disk, folder twins of one blob in the root (`D/x`, `d/x`) met git's `will not add file alias` in `check`, and three paths of one blob (`AB`, `Ab`, `ab`) in `execute` alone, after `check` said `plan ok`: both are the finding again, with its rename remediation. In the legs the NOTE is as it was, since those runs verified. The section comment, the module docstring's `check` paragraph, `_case_twin_findings`' and `_entry_findings`' docstrings say so. - NIT 2: the finding says `execute` "could stop there", as file twins in the root verified on that disk. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…folder twins and three paths of one blob are `plan-case-twins`, two paths of one blob in one folder are the NOTE, and folder twins of one blob in the code leg stay the NOTE (#1161) r1189b's SHOULD 1, as lane 1 ruled: `test_a_set_of_one_blob_is_a_note_and_not_the_finding` gains folder-twins-of-one-blob-in-the-root and three-of-one-blob-in-the-root (the finding), two-of-one-blob-in-one-folder-of-the-root and folder-twins-of-one-blob-in-the-code-leg (the NOTE), and `test_only_one_blob_in_one_mode_is_left_to_the_note` gains two-of-one-blob-in-folders-in-another-case (`D/A`, `d/a`: the finding). `twins_kept` says "could stop", as the finding now does (NIT 2). Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stack step 2 of 3) Its one file, tests/test_adopt_check_throwaway_repos.py, conflicted and takes this branch's side: the merge of #1158's final head 1e71152 already brought #1158's content, and this branch's net change to that file is #1189's own. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… measured with the root NOTE's rule, 2 each: `A` and `a` and `D/A` and `D/a` verified, folder twins and three paths of one blob refused before any leg (#1161) Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… of one blob in the spec leg are the NOTE, a file against a folder of one blob and one last name in the code leg is the finding, a leg set's third path in another mode is the finding, and `execute` takes sets of one blob past `check`'s findings (#1161) Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1189 (fix/1161-check-names-case-twins) was restacked onto main by merges and gained two commits of its own since 4d40ce9: `plan-case-twins` in the root leaves to the NOTE only two paths of one blob whose names differ only in their last part, and its rows. No conflict: git merged the comment above `plan-case-twins`, where both sides changed a paragraph, by itself. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check names the case twins the plan keeps in one repository (A and a, D/x and d/x, f and F/x) as plan-case-twins on every disk, a set of one blob as a NOTE, and execute refuses the finding before any leg (#1161)check names the case twins the plan keeps in one repository as plan-case-twins on every disk, a one-blob set as a NOTE in a leg and, in the root, two paths in one folder, and execute refuses the finding before any leg (#1161)
|
(lane openRepoShape-1, 2026-10-23T22:00:54Z) r1189c, lane 1's independent re-checker (not the writer), re-checked #1189 at
The full re-checkr1189c: re-check of PR #1189 at
|
| finding | commit | where at head | evidence |
|---|---|---|---|
| SHOULD 1: the root NOTE took more than its evidence | 44ae0a0e (tool) and a131fc25 (rows) |
adopt-project.py:9352-9367 (_one_blob_twins gains leg; the root rule is :9362-9364) and :9391 (the call passes leg). Comments are at :9284-9310, :9374-9381 and :9593-9596, and the check paragraph in the module docstring is at :116-122. Rows are at tests/test_adopt_check_throwaway_repos.py:686-699 and :790-792. |
probe and rows below |
| NIT 2: "would stop" claims too much | 44ae0a0e |
adopt-project.py:9416 ("could stop there before any leg exists"); the test sentence is at tests/test_adopt_check_throwaway_repos.py:527 |
grep -rn "would stop there before any leg" finds nothing in the repo |
NIT 3: body row for D/x/d/x lacks "own blobs" |
body, refreshed 21:30Z | the table now has "D/x/d/x in the root, own blobs" and a separate "one blob" row |
gh pr view 1189 |
The body's After paragraph no longer says plan ok for every one-blob shape. It says the NOTE covers every such set in a leg and, in the root, only two paths whose names differ only in their last part.
2. The rule in the root
The code is adopt-project.py:9362-9364:
if leg == "root" and (len(named) != 2 or len({path.rpartition("/")[0] for path in named}) != 1): return FalseThe parent folders are compared as written, not folded, so D/A against d/a is not the NOTE.
Pinning rows. I ran 22 ids at head: 10 of test_a_set_of_one_blob_is_a_note_and_not_the_finding, 8 of test_only_one_blob_in_one_mode_is_left_to_the_note, 2 of test_every_path_of_a_leg_set_is_asked_its_mode, test_the_one_blob_note_is_the_first_of_the_entries_notes and test_execute_takes_sets_of_one_blob_past_checks_findings. Result (runs/rows-head.txt): 22 passed in 16.83s.
My probe. The script is probe_root_rule.py. It builds a one-blob source with the suite's own helpers (a_source, with_paths(own_blobs=False), planned) and runs check. The output is in runs/probe-root-rule.txt:
=== root A and a, one blob: exit 0
NOTE the plan keeps 1 set of case twins in the assembly root whose paths hold one blob in one mode, 2 paths: `A` and `a`. ...
plan ok
=== root D/x and d/x, one blob: exit 1
FINDING plan-case-twins: the plan keeps 1 set of case twins in the assembly root, 2 paths, ... `D/x` and `d/x`. ...
=== root D/A and D/a, one blob (control: one folder): exit 0
NOTE ... in the assembly root ... 2 paths: `D/A` and `D/a`. plan ok
=== root AB, Ab, ab, one blob: exit 1
FINDING plan-case-twins: ... in the assembly root, 3 paths, ... `AB`, `Ab` and `ab`.
=== root D/A and d/a, one blob: exit 1
FINDING plan-case-twins: ... in the assembly root, 2 paths, ... `D/A` and `d/a`.
=== code leg src/D/x and src/d/x, one blob: exit 0
NOTE ... in the code leg ... `src/D/x` and `src/d/x`. plan ok
Root-rule mutants f01 to f10, run by me in clone mut2 (runs/mf.txt). Each anchor was unique, each compiled, and each blob was restored. All 10 were killed:
| mutant | killed by |
|---|---|
| f01 (root shape not asked) | folder-twins-of-one-blob-in-the-root |
| f02 (two paths only) | folder-twins-of-one-blob-in-the-root |
| f03 (one folder only) | three-of-one-blob-in-the-root |
| f04 (every repository) | one-blob-in-the-code-leg |
| f05 (top level only) | two-of-one-blob-in-one-folder-of-the-root |
| f06 (folders folded) | folder-twins-of-one-blob-in-the-root |
| f07 (last parts differ) | two-of-one-blob-in-folders-in-another-case |
| f08 (three taken) | three-of-one-blob-in-the-root |
| f09 (spec leg as root) | folder-twins-of-one-blob-in-the-spec-leg |
| f10 (sentence "would") | test_check_names_each_twin_git_refuses_and_each_set_it_holds |
3. The new rows of 58aa9d2b: the brief's premise does not hold
The brief says the rows "fail on a131fc25's tool". They do not, because the tool's code is the same:
a131fc25:adopt-project.pyis blob4ce5f22c, the same blob as44ae0a0e's.git diff a131fc25 HEAD -- adopt-project.pyhas only#comment lines changed (22 of them, frome45e1225). There are no non-comment changes.
I ran the swap as asked (swap.py, runs/swap.txt). It put a131fc25's tool into the clone and ran the 5 new ids: 5 passed in 6.67s, then restored. The clone's tool blob was 5fac9479, equal to HEAD's, and git status --short was empty.
So these rows are not base-failing rows. They are mutant killers, and they were measured as that.
-
With the new rows (full file at head, my run
runs/m5head.txt), all five mutants are killed by the row the body names:mutant killing row(s) b02 a-folder-and-a-file-of-one-blob-in-the-code-legb18 a-folder-and-a-file-of-one-blob-in-the-code-legb06 test_every_path_of_a_leg_set_is_asked_its_mode[executable]and[symlink]b17 test_execute_takes_sets_of_one_blob_past_checks_findingsf09 folder-twins-of-one-blob-in-the-spec-leg -
Without them (
a131fc25's test file swapped in,runs/m5old.txt), all five survive:86 passed, 2 skippedfor each. So the rows are what kills them. -
After every mutant, the tool and the test file blobs equalled HEAD's (
restored ... True×15 at head and ×5 under old tests). -
Base against head. Tool
4c7b8acdwith head's tests, whole file (runs/base-throwaway-full.txt):38 failed, 53 passed, 2 skipped.- That is the body's "by addition, 38". The failures are 14 + 10 + 8 + 2 + 1 + 1 + 1 + 1.
- Of the 5 new rows, 4 fail at base.
test_execute_takes_sets_of_one_blob_past_checks_findingspasses at base, as the body says. - The two control tests are not in the failed list.
4. The restack
git diff ef619c74 6a31aea7andgit diff 4c7b8acd a131fc25dare byte-identical:cmpis silent, and both have sha25604b2909a71bc9bec...and 48822 bytes. The changed lines alone also match (9b9429be...). Each is3 files changed, 686 insertions(+), 27 deletions(-).git diff --shortstat ef619c74 58aa9d2bgives744 insertions(+), 27 deletions(-), the body's "at the head, 744 and 27".adopt-project.pyand the throwaway test file are the same blobs atef619c74and4c7b8acd(ed9c8b1d,770c7b7c).a619be9bhas parentsa131fc25and1e71152d. It touches 7 files and none of the PR's three.1e71152d's tree equalsef619c74's (aa3caf21).6a31aea7has parentsa619be9bandef619c74. Its tree equals its first parent's tree (bcf97e48).- tests/test_adopt_check_throwaway_repos.py: rows pin that
refused_pathsholds each case twin under its own name and blob, or names it, withcore.ignorecasetrue or false, and thatchecknames the twins git refuses and none it holds (#1033) #1158 is MERGED asef619c74a9bb(2026-10-23T15:40:50Z).
5. The body's claims
- Title: 249 characters by
len()(cap 250), in the repo's style, ending(#1161). - Closing form:
grep -niE '(clos|fix|resolv)...#N'finds one hit, line 1Closes #1161.closingIssuesReferencesis[1161]. No other close, fix, resolve or Covers form appears. - PR state: DRAFT, milestone v1.2.1, base
main. The "Stacked on tests/test_adopt_check_throwaway_repos.py: rows pin thatrefused_pathsholds each case twin under its own name and blob, or names it, withcore.ignorecasetrue or false, and thatchecknames the twins git refuses and none it holds (#1033) #1158 ... restacked" paragraph is correct. - Counts:
- base 38 failed / head 91 passed + 2 skipped, as above;
- 22 rows ids at head: all pass;
- FUSE counts reconcile with
f1189b/runs/fuse-f.txt. Rounds f1 and f2 are the 2 counted runs. In each,A/aandD/A/D/agavecheckthe NOTE andexecute"adoption verified".D/x/d/xandAB/Ab/abgaveplan-case-twinsandplan-file-reencodedincheck, andexecuteexit 2REFUSED plan-case-twins. Round f3 was killed and is not counted. I did not rerun FUSE.
- Mutant tally 36 of 36: the body's own record reconciles:
cM.txthas 31 KILLED and 5 SURVIVED,cM-f1189c.txthas 5 KILLED, and 36 restores read True. I re-ran 15 of the 36 myself (b02, b06, b17, b18, f01-f10) and all 15 were killed. I did not re-run the other 21 (m01, m03, m04, m11, m15 and b01, b03-b05, b07-b16, b19-b21).
6. The two files at head
tests/test_adopt_check_throwaway_repos.py:91 passed, 2 skipped in 92.95s(runs/head-throwaway.txt).tests/test_adopt_make_reads_first.py:33 passed in 44.64s(runs/head-mrf.txt).- Both match the body (119 passed + 2 skipped together).
Extra run: the throwaway file under a 3.14-like sitecustomize shim at head (runs/head-shim.txt, shim in shim/sitecustomize.py) gave 91 passed, 2 skipped in 91.20s. is_file, exists and is_dir answer False to any OSError in it; I checked its __module__ is sitecustomize. The body says the shim run was made only at 4d40ce9f, so this closes that gap for head. These runs used the probe env. Because of the $(...) expansion on the rows run, I ran those ids with it; all 22 ran and passed.
New findings
N1 (NIT, a note for landing, not a PR defect): main has moved since the restack.
- The body says origin/main "was
ef619c74".origin/mainis now0ed3e57f(13 commits). - Two of those commits touch the PR's files:
b7edd8816(checkasks the split's own clone...) and0ed3e57f8(execute's NEXT forGNUmakefile/makefile). They changeadopt-project.pyandtests/test_adopt_check_throwaway_repos.py. git merge-tree --write-tree origin/main 58aa9d2bis clean (treed7795ebf). A realgit mergein my clonemrgauto-merged both files to the same tree.- I did not run the two test files on that merge, so the textual result is clean but untested.
- Fix: the landing procedure's H0 (a gate on the merge result) is the answer. Make the gate cover the merged tree, since
0ed3e57fconcernsmakefile, whichtest_adopt_make_reads_first.pyand adopt-project.py:checknames the case twins the plan keeps in one repository asplan-case-twinson every disk, a one-blob set as a NOTE in a leg and, in the root, two paths in one folder, andexecuterefuses the finding before any leg (#1161) #1189's two changed params both hold. The clonemrgis at/workspace/projects/openRepoShape-worktrees/_lane1-scratch/r1189c/mrg.
N2 (NIT, body prose): the Tests section's "10 rows" line. It speaks of "the 10 rows of ... then in it" against 8684af86. I did not measure that run and the arithmetic is as r1189b left it. No change needed, but no one has re-measured it.
Not covered
- The predicted-merge gate (N1) was not run.
- The first-round and
prev/prevwruns, and the 21 mutants listed above, were not re-run. The FUSE disk was not re-measured, so real APFS and NTFS are not measured either. - Not run:
test_scaffold_leg_path_collisions.py, the fourtest_shape_doctor.pyshards and the doc trio (test_agents_md_codes,test_cli_reference,test_repo_hygiene). The body's counts for them were not re-measured. - The probe's rows run used one
$(tr ...)expansion of an ids file instead of the line-by-line rule; the result was 22 of 22.
Lane: openreposhape-1 (openRepoShape-1)
Lane: openreposhape-1 (openRepoShape-1)
Merges main at 11560eb into fix/1161-check-names-case-twins at 58aa9d2 (merge-base ef619c7, #1158's squash). One conflict, tests/test_adopt_check_throwaway_repos.py, in the module docstring: #1189 and #1169 each added a paragraph after #1033's. Both are kept, #1161's first, because it opens "so `check` passed twins", which follows from #1033's measurement, then #1169's "#1136 followed". Every row and helper of both sides auto-merged untouched. adopt-project.py auto-merged: both sides change `_entry_findings` (#1189 its case twins after the source path findings; main the `GIT_ATTR_SOURCE` and split-clone questions before `_paths_git_refuses`, and `_submodule_findings`' `unasked`) and the `check` paragraph of the module docstring, in separate lines. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… leave out the trees that keep case twins, which execute now refuses as plan-case-twins (#1161) `test_the_printed_bootstrap_runs_in_the_clone_next_makes` (#1121) ran a real `execute` at `[Makefile+makefile]` and `[all-three]`, sources that keep `Makefile` and `makefile` side by side in the root, each with its own blob. Since #1161 `execute` refuses that set of case twins before any leg as `plan-case-twins`, so there is no clone to run `make` in: at the merge 5c4fcd9 both failed (`REFUSED plan-case-twins`, `assert 2 == 0`). Its parametrize now leaves out the rows `keeps_twins` (from `test_adopt_make_reads_first`) names, as #1189 did there in 8684af8, and its docstring says why, and that `test_execute_refuses_makefile_beside_makefile_before_any_leg` pins the refusal for those trees. Every other row is as it was: the test runs 7 rows, the file's other functions all of ROWS. Measured on CPython 3.12, the whole file: at 5c4fcd9 2 failed, 38 passed; with this change 38 passed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e out the trees that keep case twins, which check names and execute refuses as plan-case-twins (#1161) `test_the_sentences_are_true_of_what_make_does_in_the_clone` (#1105) ran a real adoption at `[Makefile+makefile]` and `[all-three]`, and `test_where_the_file_make_reads_includes_makefile_the_merge_alone_is_enough` (#1105) at `[makefile]`: sources that keep `Makefile` and `makefile` side by side in the root, each with its own blob. Since #1161 `check` names that set of case twins as `plan-case-twins` and `execute` refuses it before any leg, so there is no clone to run `make` in: at the merge 47478d5 all three failed at `adopt_with_legs_mounted_away`'s `check` (`FINDING plan-case-twins: ... `Makefile` and `makefile``, `assert 1 == 0`). Both parametrizes now leave out the rows `keeps_twins` (from `test_adopt_make_reads_first`) names, as 7d2f651 did in `test_adopt_next_make_reads_first.py`, and each docstring says why, and that `test_execute_refuses_makefile_beside_makefile_before_any_leg` pins the refusal for those trees. Every other row is as it was: the clone test runs 5 of CLONES, the include test its `[GNUmakefile]` row, and the file's other functions, which call `_predict_collisions` and `follow_ups_for` and run no `check`, all their rows, `[Makefile+makefile]` and `[all-three]` among them. Measured on CPython 3.12, the whole file: at 47478d5 3 failed, 28 passed; with this change 28 passed, the same 28 node ids. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|



Closes #1161
Stacked on #1158 (head
4c7b8acd), which landed asef619c74; this branch was restacked onto it by three merges:a619be9b, tests/test_adopt_check_throwaway_repos.py: rows pin thatrefused_pathsholds each case twin under its own name and blob, or names it, withcore.ignorecasetrue or false, and thatchecknames the twins git refuses and none it holds (#1033) #1158's final head1e71152d: clean, bringing in 7 files of main's and none of this PR's;6a31aea7, tests/test_adopt_check_throwaway_repos.py: rows pin thatrefused_pathsholds each case twin under its own name and blob, or names it, withcore.ignorecasetrue or false, and thatchecknames the twins git refuses and none it holds (#1033) #1158's squashef619c74: its one conflict, intests/test_adopt_check_throwaway_repos.py, took this branch's side, and the tree after is the tree before;ef619c74), so no commit.The net diff is identical:
git diff ef619c74 6a31aea7is byte for bytegit diff 4c7b8acd a131fc25(sha25604b2909a71bc, 3 files, 686 insertions, 27 deletions); at the head, 744 and 27.adopt-project.pyandtests/test_adopt_check_throwaway_repos.pyatef619c74are the blobs of4c7b8acd, so "before" and "base" below,4c7b8acd, are what main holds. It is outside v1.2.0 by Brett Heap's ruling of 2026-10-21T10:32:04Z ("leave the follow-ups out of v1.2.0").Merge with main after #1105
47478d51merges main at12122952(#1105's squash) into this branch at7d2f651e, the head the next section ends at. Their merge-base is11560eb9, #1223's squash, and main holds 8 commits since it: #1166, #1133, #1104, #1131, #1160, #1190, #1243 and #1105. Of them #1190 and #1105 changeadopt-project.py(#1190cmd_plan,_grouped_findings,_refuse_a_partial_clone_shortandmain; #1105_predict_collisions), in lines this PR does not touch, and none changes a test file of this PR's. The merge was clean:git merge-tree --write-treeof main and7d2f651egave tree73fffbb8, the merge's tree.+and-lines ofgit diff -U0 11560eb9 7d2f651e, this PR's delta before this round, and ofgit diff -U0 12122952 47478d51are 787 each, in the same 4 files (756 insertions, 31 deletions), and equal in order: sha256637968e30cb5for both. The whole-U0diffs (sha2563dc5ecb2fc2bande7c6e5eb30ba) differ only inadopt-project.py'sindexline and 6 of its 7 hunk headers, each 126 lines later: the lines adopt-project.py:planruns withGIT_NO_LAZY_FETCH=1, refuses a partial-clone--sourcethat lacks objects assource-partial-clone, and names a partial--pin-sourceit could read only by fetching and the fetch that makes it whole (#1162) #1190 and shape_materialize.py and the pinned AGENTS-shape.md: where the root keeps aGNUmakefileor amakefile, which GNU make reads first, the Makefile collision follow-up no longer says the shape's make targets arrive with the merge, and the pinned file saysmakereads the first of the three and the block reaches it only once that file holds it (#1048) #1105 added above this PR's code.GNUmakefileor amakefile, which GNU make reads first, the Makefile collision follow-up no longer says the shape's make targets arrive with the merge, and the pinned file saysmakereads the first of the three and the block reaches it only once that file holds it (#1048) #1105's failed at the merge, and472944b5cuts them, by the agreement on docs/release-notes/v1.0.0.md: the v1.0.0 release notes, a draft for Brett to amend before he tags, saying what 1.0 contains and promises, how to pin it by commit, what changed on the way grouped by the five tracking areas, and every known limit as the input that triggers it and what the person sees (#472) #473 (issuecomment-6723000456, with lane 4's no-objection, issuecomment-6722980082) that whichever of shape_materialize.py and the pinned AGENTS-shape.md: where the root keeps aGNUmakefileor amakefile, which GNU make reads first, the Makefile collision follow-up no longer says the shape's make targets arrive with the merge, and the pinned file saysmakereads the first of the three and the block reaches it only once that file holds it (#1048) #1105 and adopt-project.py:checknames the case twins the plan keeps in one repository asplan-case-twinson every disk, a one-blob set as a NOTE in a leg and, in the root, two paths in one folder, andexecuterefuses the finding before any leg (#1161) #1189 landed second would cut the twin rows oftests/test_adopt_collision_make_reads.py. At47478d51that file gave 3 failed, 28 passed:test_the_sentences_are_true_of_what_make_does_in_the_clone[Makefile+makefile]and[all-three], andtest_where_the_file_make_reads_includes_makefile_the_merge_alone_is_enough[makefile], run a real adoption of a source that keepsMakefileandmakefileside by side in the root, each with its own blob. This PR names that set asplan-case-twins:adopt_with_legs_mounted_away'scheckexits 1 withFINDING plan-case-twinsnamingMakefileandmakefile(assert 1 == 0), andexecuterefuses such a plan before any leg, so there is no clone to runmakein. Main has noplan-case-twins, so they could not fail this way there.472944b5leaves those rows out of the two parametrizes bykeeps_twinsfromtests/test_adopt_make_reads_first.py, as7d2f651edid intests/test_adopt_next_make_reads_first.py. Each docstring says why, and thattest_execute_refuses_makefile_beside_makefile_before_any_legpins the refusal for such trees. Every other row is as it was: the clone test runs its other 5 rows, the include test its[GNUmakefile]row, and the file's other functions, which call_predict_collisionsandfollow_ups_forand run nocheck, all their rows,[Makefile+makefile]and[all-three]among them. With it the file gives 28 passed, the same 28 ids that passed at the merge.adopt-project.py'scheckorexecuteon a tree that keeps case twins (a scan of the 9 test files main changed since11560eb9).472944b5, the delta against main is 5 files, 781 insertions and 37 deletions: the 4 files above, as they were, and the cut.472944b5, CPython 3.12, in the probe environment, each call withdf -h /workspacefirst and one basetemp:tests/test_adopt_collision_make_reads.py, whole file: at47478d51, before the cut, 3 failed, 28 passed; at472944b528 passed;tests/test_adopt_make_reads_first.pyandtests/test_adopt_next_make_reads_first.py: 71 passed (33 and 38);tests/test_adopt_check_throwaway_repos.py, whole file: 194 passed, 2 skipped, the two rows that need a disk that ignores case;tests/test_adopt_single_repository_record.pyandtests/test_scaffold_leg_path_collisions.py: 143 passed (39 and 104);tests/test_path_guard_hygiene.pyandtests/test_no_auto_maintenance.py: 179 passed, 2 skipped (the session began withGIT_CONFIG_GLOBALset);tests/test_agents_md_codes.py,tests/test_cli_reference.pyandtests/test_repo_hygiene.py: 171 passed;tests/test_shape_doctor.pyin 4 shards of 113, 112, 112 and 112 ids: 112 passed, 1 skipped; 111 passed, 1 skipped; 112 passed; 111 passed, 1 skipped. The skips are the PowerShell row, which runs only on a Windows host.Merge with main after #1169 and #1223
5c4fcd9dmerges main at11560eb9(#1223's squash) into this branch at58aa9d2b, the head r1189c read. Their merge-base isef619c74, #1158's squash, and main holds 20 commits since it: #1124, #1140, #1149, #1126, #1107, #1157, #1088, #1193, #1187, #1119, #1156, #1208, #1121, #1200, #1180, #1109, #1235, #1144, #1169 and #1223. Of them #1119, #1121, #1180 and #1169 changeadopt-project.py, and #1119 and #1169 changetests/test_adopt_check_throwaway_repos.py.tests/test_adopt_check_throwaway_repos.py, is in its module docstring: this PR's#1161:paragraph and 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's#1136 followedparagraph were each added after adopt-project.py:refused_paths'update-index -z --index-infois untested withcore.ignorecaseand case twins (aandA) on a case-insensitive disk #1033's. Both are kept, this PR's first, since it opens "socheckpassed twins", which follows from adopt-project.py:refused_paths'update-index -z --index-infois untested withcore.ignorecaseand case twins (aandA) on a case-insensitive disk #1033's measurement, and 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's after it. No row, helper or import conflicted: this PR'swith_pathsand its rows, inserted after tests/test_adopt_check_throwaway_repos.py: rows pin thatrefused_pathsholds each case twin under its own name and blob, or names it, withcore.ignorecasetrue or false, and thatchecknames the twins git refuses and none it holds (#1033) #1158's row, and main's helpers (recording_where_made,asked_where,in_the_command_scope, the constants and the import fromtest_adopt_nested_attributes) are in other lines, and the merge took both as they were.adopt-project.pymerged without a conflict. Both sides change_entry_findingsand thecheckparagraph of the module docstring, in separate lines. This PR asks_case_twin_findingsafter_source_path_findingsand adds its findings, remediation and NOTE to the lists returned. Main asks_attr_source_unaskedand_split_clone_askedbefore_paths_git_refuses, in the sametry, and passesunaskedthrough_submodule_findings. Where one of main's questions raises_Unasked,refusedis[], as it was where_paths_git_refusesraised, so_case_twin_findingsasks every path of the tree, as on this branch alone.executestill refuses the finding through_refuse_what_check_finds; main's_refuse_a_partial_clone_shortand@_lazy_fetch_off()come before it. Each side's code does what it did alone.+and-lines ofgit diff -U0 ef619c74 58aa9d2b, what r1189c reviewed, and ofgit diff -U0 11560eb9 5c4fcd9dare 771 each, in the same 3 files (744 insertions, 27 deletions), and equal as a multiset (sorted, sha25603e4a0a33b8a). In order they differ in one line only: the blank+line beside this PR's docstring paragraph comes before it in the reviewed delta, where the base held nothing after adopt-project.py:refused_paths'update-index -z --index-infois untested withcore.ignorecaseand case twins (aandA) on a case-insensitive disk #1033's paragraph, and after it in the merge's, where 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's paragraph follows. In order, sha2561a97f9a715e5andc35465aefe6b.5c4fcd9d, CPython 3.12, in the probe environment, each call withdf -h /workspacefirst and one basetemp:tests/test_adopt_check_throwaway_repos.py, whole file: 194 passed, 2 skipped. That is 45 ids of this PR's 12 test functions (44 passed and the disk row that skips), 102 of main's 46 (all passed) and 49 of the 20 both kept (48 passed, 1 skipped). The 2 skips are the two rows that need a disk that ignores case;tests/test_adopt_make_reads_first.py: 33 passed;tests/test_adopt_single_repository_record.py: 39 passed;tests/test_scaffold_leg_path_collisions.py: 104 passed;tests/test_path_guard_hygiene.pyandtests/test_no_auto_maintenance.py: 179 passed, 2 skipped (the session began withGIT_CONFIG_GLOBALset);tests/test_agents_md_codes.py,tests/test_cli_reference.pyandtests/test_repo_hygiene.py: 171 passed;tests/test_shape_doctor.pyin 4 shards of 113, 112, 112 and 112 ids: 112 passed, 1 skipped; 111 passed, 1 skipped; 112 passed; 111 passed, 1 skipped. The skips are the PowerShell row, which runs only on a Windows host.7d2f651emends them, on lane 1's word.tests/test_adopt_next_make_reads_first.py(adopt-project.py:execute's NEXT printspython3 scripts/bootstrap.pywhere GNU make at the root reads a keptGNUmakefileormakefile, and its NOTE names that file and what it takes formake bootstrapto run there (#1047) #1121) keepsMakefileandmakefileside by side in two rows, and at5c4fcd9dit gave 2 failed, 38 passed.test_the_printed_bootstrap_runs_in_the_clone_next_makes[Makefile+makefile]and[all-three]run a realexecuteof a source that keeps both names, each with its own blob, which this PR refuses before any leg (REFUSED plan-case-twins,assert 2 == 0). Main has noplan-case-twins, so they could not fail this way there.7d2f651eleaves those two rows out of that test, bykeeps_twinsfromtests/test_adopt_make_reads_first.py, as8684af86did in that file, since there is no clone to runmakein. Its docstring says why, and thattest_execute_refuses_makefile_beside_makefile_before_any_legpins the refusal for those trees. Every other row is as it was: the test runs its other 7 rows, and the file's other functions run all ofROWS. At7d2f651e, CPython 3.12, in the probe environment:tests/test_adopt_next_make_reads_first.py: 38 passed, the 2 rows gone and none skipped;tests/test_adopt_make_reads_first.py: 33 passed;tests/test_adopt_root_symlinks.py206 passed, 3 skipped (reftable needs git 2.45);tests/test_adopt_partial_clone_source.py38 passed;tests/test_adopt_pins.py152 passed;tests/test_adopt_nested_attributes.pyandtests/test_adopt_reencoded_files.py126 passed;tests/test_path_guard_hygiene.pyandtests/test_no_auto_maintenance.py179 passed, 2 skipped; the doc trio 171 passed (both run on7d2f651e's file before its commit, blob38cde83b).tests/for test trees that keep case twins, and thatcheckorexecuteis run on, found no others: onlytests/test_adopt_check_throwaway_repos.py, whose twins are the point, and the two make-reads-first files. The other twin spellings it met are a source path beside a path the shape writes (adopt-project.py:checkpasses a kept path that is a case twin of a file the shape writes into the made root (makefilebeside the shape'sMakefile), so a clone of that root on a disk that ignores case keeps one of each pair (#1161) #1191's ground), plan values, or names.What
Measured with git 2.43, on the bench disk (keeps case) and on a FUSE disk that ignores case and keeps it (
casefoldlookups), with the work dir and every repository on it.Before (the tool at
4c7b8acd): on the bench diskcheckprintsplan okfor every scenario below, andexecuteexits 0 and verifies each one it was run on.not measuredmeans not run at4c7b8acd;(bench disk only)means the base run of the row's test, exit 0 on the bench disk, and nothing on the disk that ignores case. On the disk that ignores case:checkbeforeexecutebefore, disk that ignores casecheckafterA/ain the root, own blobsplan okplan-case-twinsA/ain the root, one blobplan okplan ok;executeverified (3 of 3 runs: 1 at4d40ce9f, 2 ata131fc25)D/A/D/ain the root, one folder, one blobplan ok(bench disk only)plan ok;executeverified (2 of 2 runs, ata131fc25)src/A/src/a, one blob, code legplan okplan ok;executeverified (3 of 3 runs)specs/A/specs/a, one blob, spec legplan okplan ok;executeverified (1 run)D/x/d/xin the root, own blobsplan okin 2 of 4 fresh runs, elseplan-file-reencoded("will not add file alias 'D/x'")plan-file-reencodedplan-case-twins(thenplan-file-reencodedwhere git refuses the alias)D/x/d/xin the root, one blobplan ok(bench disk only)plan-case-twinsand FINDINGplan-file-reencoded(2 of 2 runs, ata131fc25);executerefused before any leg, asplan-case-twins(2 of 2)AB/Ab/abin the root, one blobplan ok(bench disk only)executerefused before any leg asplan-case-twins(2 of 2 runs, ata131fc25)F/x/fin the rootplan okadopt-lost: F/x, both legs and the split already pushedplan-case-twinssrc/A,src/a,src/D/x,src/d/x, own blobs, code legplan okgit rm -r -q -- src("the following files have local modifications: src/A, src/D/x") after both legs were pushedplan-case-twinsA,D/xroot;a,d/xcode legplan okadopt-lostforAandD/x, both legs and the split already pushedplan ok(Not in this PR, (a), #1183)Aroot;aanswereddropplan okadopt-lost: A, both legs and the split already pushedplan ok(Not in this PR, (a), #1183)plan okplan okAfter:
checkexits 1 and names, one finding per repository the split makes (the root, the spec leg, the code leg), every set of source paths that one answered entry sends into one repository whose paths differ only in case, or where one path names in another case a folder above another. A set whose paths all fold to one name and hold one blob in one mode is left out of the finding: a NOTE names it, andcheckstill saysplan okfor it. In a leg that holds for every such set, folder twins (src/D/x,src/d/x) and sets of three or more included. In the root the NOTE takes only two paths whose names differ only in their last part,AandaorD/AandD/a: folder twins of one blob there (D/x,d/x), two paths whose folders are twins too (D/A,d/a) and sets of three or more (AB,Ab,ab) stay in the finding, since on the FUSE diskexecutemet git'swill not add file aliasfor them. The FINDING says why a clone on a macOS or Windows disk checks out one path of each set, and thatexecutecould stop there before any leg exists, at git'swill not add file alias, or after both legs are pushed, asadopt-lostor atgit rmmounting the legs, soexecuterefuses them on every disk. Its remediation says to rename all but one twin of each set in the source, commit, and re-runplan. It no longer offers to answer the twins into different repositories: on a disk that ignores caseexecuteloses those too (#1183).executeruns the same checks first, so it refuses such a plan before it makes either leg.executeon every disk, andchecknames itplan-case-twinsand exits 1, unless its paths fold to one name and hold one blob in one mode (the NOTE). In the root the NOTE takes only two paths whose names differ only in their last part, so folder twins and sets of three or more of one blob there are refused too. A source that keepsMakefileandmakefileside by side in the root is one: before,checkprintedplan ok, and on a disk that keeps caseexecutesplit the source and verified it. A source that keeps onlymakefileis not, though the shape writes the root'sMakefilebeside it (Not in this PR, (c), adopt-project.py:checkpasses a kept path that is a case twin of a file the shape writes into the made root (makefilebeside the shape'sMakefile), so a clone of that root on a disk that ignores case keeps one of each pair (#1161) #1191). Two params oftests/test_adopt_make_reads_first.pyheldMakefileandmakefile,[Makefile+makefile]and[all-three], and they changed:test_plan_writes_the_line_and_check_prints_it, at those two params, now expects exit 1, theplan-case-twinsfinding namingMakefileandmakefile, and the follow-up linecheckstill prints (its other params still expect exit 0).test_the_split_message_names_the_file_make_reads_in_the_cloneno longer runs those two params, sinceexecuterefuses their source before any leg and there is no clone to runmakein; the newtest_execute_refuses_makefile_beside_makefile_before_any_leg, at the same two params, asserts that refusal asplan-case-twins: no bare repository, no assembly or leg clone in the work directory, no split branch in the source.core.ignorecasewherecheckruns says nothing about the reader's disk.Why
core.ignorecasewherecheckruns says nothing about the reader's disk.executeon a disk that ignores case loses them too, asadopt-lostafter both legs are pushed (Not in this PR, (a), adopt-project.py: on a disk that ignores case,executeloses a case twin the plan keeps in the root when its twin goes to a leg #1183);checkpasses a kept path that is a case twin of a file the shape writes into the made root (makefilebeside the shape'sMakefile), so a clone of that root on a disk that ignores case keeps one of each pair (#1161) #1191).casefold), and a file against a folder (a path'scasefoldequals a folder above another path).D/xbesided/yis no finding: on any disk the two folders merge into one and no path is lost.the following paths have collided, andgit statusis clean (r1189). A file against a folder, two modes (100644beside100755), submodules (whose object is a commit, not a blob), or one path of another blob keep the set in the finding.checkalready prints NOTEs beside its findings (the pins' digests, the symlink probe, relative submodule urls), so the set is named in one, the first of the NOTEs_entry_findingsreturns.executeprints no NOTE and does not refuse it.4d40ce9f: adopt-project.py:checknames the case twins the plan keeps in one repository asplan-case-twinson every disk, a one-blob set as a NOTE in a leg and, in the root, two paths in one folder, andexecuterefuses the finding before any leg (#1161) #1189 issuecomment-6364543347, SHOULD 1). In the root it takes two paths whose names differ only in their last part,AandaorD/AandD/a, the shape that verified there in every run measured. Every other set of one blob there is the finding, with its rename remediation: on the FUSE diskD/xandd/x, andAB,Abandab, met git'swill not add file alias, which the adopt-project.pycheck: a kept or moved file other than.gitattributeswhose blobgit addwould re-encode passes, andexecuteendsadopt-lostor dies mounting the legs after both legs are pushed #654 rehearsal reports asplan-file-reencoded; andD/Abesided/ahas folders that are twins too. Every such set measured in a leg verified. Whatexecutedid on the FUSE disk:adoption verifiedin every run: filesAanda, 4 runs (1 at4c7b8acd, r1189's; 1 at4d40ce9f; 2 ata131fc25); filesD/AandD/a, 2 runs ata131fc25; for r1189b, 1 run of symlinks to one target (A,a) and 1 of executables (A,a);adoption verifiedin every run: in the code leg, filessrc/Aandsrc/a, 5 runs (2 at4c7b8acd, 3 with this check), and for r1189b 1 run each of symlinks to one target (src/A,src/a) and of folder twins (src/D/x,src/d/x), alone and beside folders that only merge; in the spec leg, filesspecs/Aandspecs/a, 1 run;4d40ce9f, folder twins (D/x,d/x), alone and beside folders that only merge, metplan-file-reencodedincheck(1 run each), and three paths (AB,Ab,ab) passedcheckand met it inexecute, before any leg (1 run); ata131fc25, 2 runs each of folder twins and of three paths:checkprintedplan-case-twinsandplan-file-reencoded, andexecuterefused both before any leg asplan-case-twins.checkand loses them on a disk that ignores case after both legs are pushed (r1189 on FUSE:checkexit 0,executeexit 1,adopt-lostforAandD/x).plan-path-git-refusesalready names them), and so aredropand unanswered entries: the code reads each entry's answer with_covering, as_PlannedRoot._keepdoes, and folds withcasefold, as_KeptTreedoes. An unanswered twin is named byplan-unresolvedalone, with no twins remediation._named_and_counted, in the finding and in the NOTE alike.check'splan-*codes (AGENTS.md names none; docs/cli.md is--help, which takes the docstring's first line only). The module docstring'scheckparagraph and_entry_findings' docstring name the new check and the NOTE.Tests
4c7b8acd) against head, whole oftests/test_adopt_check_throwaway_repos.py, CPython 3.12:a131fc25: base 34 failed, 52 passed, 2 skipped; head 86 passed, 2 skipped. The 34 are the 29 that failed at4d40ce9f(base 29 failed, 52 passed, 2 skipped; head 81 passed, 2 skipped) and the 5 rowsa131fc25added;58aa9d2b: 91 passed, 2 skipped. Its 5 new ids, with the 8 rows oftest_a_set_of_one_blob_is_a_note_and_not_the_findingthat were there, were run at base alone, 13 ids: 12 failed, 1 passed (theexecuterow, which passes at base, wherecheckhas no NOTE forexecuteto refuse). The whole file was not run at base at58aa9d2b; by addition, 38 of its ids fail there.4d40ce9f: the same 29 fail on base (the shim loaded in 192 processes) and head passed 81, 2 skipped (193 processes). Not made, for the rounds since: the shim run ata131fc25and at58aa9d2b; theprevrun, the tool at4d40ce9fagainst this head's tests; and theprevwrun, the same with the tests'twins_keptsentence set back to "would", which would have told the rows of the root rule from the sentence's.a131fc25and 4 more at58aa9d2b:test_check_names_each_twin_git_refuses_and_each_set_it_holds(tests/test_adopt_check_throwaway_repos.py: rows pin thatrefused_pathsholds each case twin under its own name and blob, or names it, withcore.ignorecasetrue or false, and thatchecknames the twins git refuses and none it holds (#1033) #1158's row, renamed: it now also expects the twins finding forA/a,D/x/d/x,F/x/f);test_check_names_the_case_twins_the_plan_keeps_in_one_repository, 14 params: file-twins, folder-twins, a-folder-and-a-file, a-file-and-a-folder, three-twins-none-in-lower-case, twins-in-the-spec-leg, non-ascii-twins-in-the-code-leg, two-sets-in-the-code-leg-set-by-set, a-set-in-each-repository, ten-named-and-the-rest-counted, and, for r1189's SHOULD 6, a-file-and-a-folder-in-the-code-leg (src/E,src/e/x), a-file-and-a-folder-two-levels-above (f,F/G/x), sharp-s-folded-as-ss (src/straße,src/STRASSE, known limit 1) and one-set-of-four; each also asserts the whole remediation line, which offers no answer into different repositories, and no NOTE;test_twins_come_before_coverage_and_name_executes_refusal(r1189's SHOULD 6): beside a path no entry covers,checklistsplan-case-twinsthenplan-uncovered, andexecute's refusal code isplan-case-twins;test_a_set_of_one_blob_is_a_note_and_not_the_finding, 10 params (r1189's SHOULD 5, and r1189b's SHOULD 1):A/aof one blob is the NOTE andplan ok, exit 0; of two blobs, the finding, exit 1; three paths of one blob in the code leg, the NOTE;F/x/fof one blob, the finding; and, in the root, folder twins of one blob (D/x,d/x) and three paths of one blob (AB,Ab,ab) are the finding, and two paths in one folder (D/A,D/a) are the NOTE; folder twins of one blob are the NOTE in the code leg (src/D/x,src/d/x) and in the spec leg (specs/D/x,specs/d/x); and a file against a folder of one blob and one last name in the code leg (src/F/f,src/f) is the finding;test_only_one_blob_in_one_mode_is_left_to_the_note, 8 params,_case_twin_findingsasked itself: one blob in two modes, submodules at one commit, and one of three paths in another blob are the finding; symlinks to one target are the NOTE; one repository can hold a set of each; two NOTE sets are named set by set;STRASSEandstraßeof one blob are one name for the NOTE, ascasefoldfolds them; and, in the root,D/Abesided/aof one blob is the finding, as their folders are twins too. At base they fail because the function is not there;test_every_path_of_a_leg_set_is_asked_its_mode, 2 params, the same call over the code leg:src/AB,src/Abandsrc/abof one blob, the last an executable or a symlink, hold two modes, so the finding and no NOTE, whichever path differs;test_the_one_blob_note_is_the_first_of_the_entries_notes:_entry_findingslists the twins' NOTE before the pins';test_execute_refuses_case_twins_before_any_leg.8684af86, with the test file ofe4f8cc51, the 10 rows oftest_a_set_of_one_blob_is_a_note_and_not_the_findingandtest_only_one_blob_in_one_mode_is_left_to_the_notethen in it fail:checknamedA/aof one blob in the root, and three paths of one blob in the code leg, asplan-case-twins, where they are now the NOTE; the two finding rows fail on the sentence NIT 7 changed; the 6 direct calls fail as the function returned no NOTEs.test_paths_no_one_repository_holds_as_one_name_are_no_finding(4 params: twins in different repositories, a twin dropped,D/xbesided/y, no twins), andtest_a_twin_still_unanswered_is_named_by_plan_unresolved_alone. Its docstring now says the first two are lost on a disk that ignores case (adopt-project.py: on a disk that ignores case,executeloses a case twin the plan keeps in the root when its twin goes to a leg #1183), not held apart.test_execute_takes_sets_of_one_blob_past_checks_findingspasses on both too: it runscmd_executein the process on a plan keepingAandain the root andsrc/Aandsrc/ain the code leg (plan ok), and sees it go on pastcheck's findings to the next step, where a sentinel stops it before any leg exists (no remotes directory); at base there is no NOTE forexecuteto refuse.test_on_a_disk_that_ignores_case_check_names_the_twins_tooskips unlessgit initin the work dir writescore.ignorecase=true. On the FUSE disk it passes at4d40ce9f, besidetest_on_a_disk_that_ignores_case_each_twin_is_held_or_named(2 passed); r1189 saw it fail at base.a131fc25, in one chain, and the 5 that survived there at58aa9d2b; every blob was restored.a131fc25(r1189's five survivors at8684af86, killed by the SHOULD 6 rows: m01 (casefoldaslower) by sharp-s-folded-as-ss, m03 (the top level only) by a-file-and-a-folder-in-the-code-leg, m04 (the immediate parent only) by a-file-and-a-folder-two-levels-above, m11 (the finding listed last) by the coverage row, m15 (len(twins) // 2) by one-set-of-four; and b01-b21: the NOTE's conditions, its text, its place among the NOTEs, the remediation, the sentence andexecute's refusal; b21 is now on the sentence'scould); and this round's 10 for the root rule and the sentence (f01-f10): the root's shape not asked; two paths only; one folder only; every repository, not the root alone; the top level only; the folders folded; last parts that differ; three paths taken; the spec leg treated as the root; the sentence back to "would".a131fc25, all killed at58aa9d2b:executerefusing the NOTE) and f09 (the spec leg treated as the root), expected, bytest_execute_takes_sets_of_one_blob_past_checks_findingsand by the row folder-twins-of-one-blob-in-the-spec-leg;test_every_path_of_a_leg_set_is_asked_its_mode;_one_blob_twins,src/F/fbesidesrc/fof one blob, a file against a folder, and the row a-folder-and-a-file-of-one-blob-in-the-code-leg kills both. In the root they are unreachable, as the test for two paths in one folder returns first.4d40ce9fand6e3ebfa6: 32 of 32 killed, r1189's 17 (m01-m17) and the NOTE's own 15 (n01-n15). n15 survived at6e3ebfa6and is killed at4d40ce9fby sharp-s-and-ss-of-one-blob; n13 survives withouttest_the_one_blob_note_is_the_first_of_the_entries_notes, which is why that row exists. Those 32 were not run again at this head, nor were the first round's 23, of the code as it was then.df -h /workspacefirst and one basetemp, CPython 3.12. The tool's code has not changed sincea131fc25but for a comment (e45e1225), so the runs made ata131fc25stand for it (they ran before the restack, which brought in files of main's that this PR does not touch; the make-reads-first file ran again after it, at6a31aea7); two calls were run again at the head:58aa9d2b:test_path_guard_hygiene,test_agents_md_codes,test_cli_reference,test_repo_hygiene,test_no_auto_maintenance: 278 passed, 2 skipped (the session began withGIT_CONFIG_GLOBALset). Ata131fc25it was 277 passed, 2 skipped; the one more istests/test_repo_hygiene.py::test_the_one_line_install_is_the_same_everywhere[docs/release-notes/v1.2.0.md], a file the restack brought in from main;58aa9d2b:tests/test_adopt_check_throwaway_repos.py, whole file: 91 passed, 2 skipped;6a31aea7, after the restack: that file withtests/test_adopt_make_reads_first.py: 119 passed, 2 skipped (86 + 33);a131fc25:tests/test_adopt_make_reads_first.py, whole file: 33 passed;tests/test_scaffold_leg_path_collisions.py: 104 passed;tests/test_shape_doctor.pyin 4 shards: 111 passed, 1 skipped; 112 passed; 111 passed, 1 skipped; 111 passed, 1 skipped. The skips are the PowerShell row, which runs only on a Windows host;a43edbdc(test_adopt_make_reads_firstat8684af86): g2test_adopt_root_shape_paths121 passed,test_adopt_root_symlinks205 passed, 3 skipped (reftable needs git 2.45); g3test_adopt_contracts_dir,test_adopt_plan_leg_path_help,test_adopt_device_names,test_adopt_finding_spelling,test_adopt_check_plan_name,test_adopt_printed_sentences,test_adopt_leg_paths: 702 passed;test_adopt_execute_refusals181 passed;test_adopt_pinsandtest_adopt_source_clone_record250 passed;test_adopt_answered_legs,test_adopt_leg_path_collisions,test_adopt_manifest_values,test_adopt_mount_ignored,test_adopt_relative_submodule_url,test_adopt_root_manifest104, 108 and 61 passed;test_adopt_plan_yaml_quoting,test_adopt_submodule_url_answers562 passed,test_adopt_symlink_target_bytes130 passed. Not re-run: the rounds since change only whatplan-case-twinssays, its remediation, and which sets of one blob are a NOTE. Those files met no set of twins ata43edbdc(they passed with the finding live there), so they meet neither the finding nor the NOTE now.Known limits
casefoldis full case folding, soßandssare named as a set, though a disk may hold them apart (rowsharp-s-folded-as-ss).ébesideeand a combining accent is no finding, though a macOS disk takes them as one name.casefoldlookups) with git 2.43, 2 runs each for the sets this round added. Real APFS and NTFS were not measured.core.ignorecase=true, where folder twins (D/x,d/x) giveplan-case-twins, thenplan-file-reencoded(3 of 3 runs).Aandathere giveplan-case-twinsalone (r1189).Not in this PR
executeloses a case twin the plan keeps in the root when its twin goes to a leg #1183: case twins the plan sends to different repositories (A,D/xin the root;a,d/xin the code leg), and a twin it drops beside one it keeps (Ain the root,aanswereddrop).checkpasses both, and on a disk that ignores caseexecuteexits 1 asadopt-lostafter both legs and the split are pushed (r1189, at4c7b8acdand at8684af86).plan-file-reencodedwhere git refused them as a file alias #1184: the rehearsal's alias refusal. On a disk that ignores case, folder twins meet git'swill not add file alias 'D/x' ('d/x' already exists in index)in the rehearsal, andcheckreports it asplan-file-reencoded, whose words say the bytes were re-encoded. With this PRplan-case-twinscomes first, but the second finding still misnames the refusal.checkpasses a kept path that is a case twin of a file the shape writes into the made root (makefilebeside the shape'sMakefile), so a clone of that root on a disk that ignores case keeps one of each pair (#1161) #1191: twins a kept path makes with a file the shape writes into the root:Makefile,README.md,AGENTS.md,CLAUDE.md,.gitmodules. A source that keepsmakefilealone, orreadme.mdalone without aREADME.md, leaves such a pair in the made root, andcheckandexecutepass it (r1189, on the bench disk:adoption verified, the split root holdingMakefileandmakefile). Naming them would reverse adopt-project.py: five printed sentences say what the run does not do: the Makefile follow-up beside a root Makefile, thecontracts/-only read-across path,execute would stop there,a path names no attribute, and WHAT WILL HAPPEN under--local-remote-dir#906 and adopt-project.py: a kept lower-casemakefileorGNUmakefilestill gets "the root Makefile wiresCONTRACTS_DIR", and README.md'sCONTRACTS_DIRsentence has no kept-Makefile case (follow-ups to #906) #977's design, which writes the shape'sMakefilebeside a source'smakefile, so lane 1 rules on it there.Lane: openreposhape-1 (openRepoShape-1)
🤖 Generated with Claude Code