Repository navigation
adopt-project.py: check names each case twin at the shape's copy, above it and at the manifest, before a link there or after it, and at a pin's file whose exact spelling holds nothing, as _KeptTree keeps every row, not the first (#1138) - #1170
Conversation
…ings docstring (N3), shape_materialize.py's two mentions of a bad link at the copy (N4) and their test; N1 left, mutants and gate not yet run Work as it stands at a coordinator checkpoint, not a finished change. Built: three cases in tests/test_adopt_symlink_target_bytes.py (the case twin of the README copy, a file at the shape's own pin after a declared pin's link, a file at the Makefile copy after the README copy's link), each asserting the second finding is still made; they pass on head and on base 2a119f2. The _root_symlink_findings docstring names the #979 exception. scripts/shape_materialize.py (not pinned or copied: in no copy list, no byte check) says in copy_in_the_way's docstring and in materialize-copy-blocked's remediation that a link plan-empty-symlink or plan-bad-symlink-target names is that finding's alone; a test in tests/test_adopt_root_shape_paths.py pins both. Not run yet: M12-M14 against this head, the gate. N1 is left (see the PR body when it is written). Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…er takes any other path's finding with it, a case twin of the copy, a later pin file or a later copy, and the `_root_symlink_findings` docstring and shape_materialize.py name the #979 exception (#1089) On top of the checkpoint 702ce0f: the Makefile-copy case answers the link's own entry (shape/README.md) root, as plan proposes spec for it by its .md extension, so the link is kept in the root and the copy skip that M14 breaks is reached; M14 is killed now. A lost second finding fails with the findings listed, not an unpack error. Part of #1089: SHOULD-2, N3 and N4. N1 (a location tail on the link's own finding) is left. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…kip-tests Main at 4ed7c7b (#1091, #1101, #1102). tests/test_adopt_root_shape_paths.py conflicted where both sides added a test after test_the_materializer_never_writes_a_copy_over_what_is_there: main's test_the_materializer_stops_at_a_link_at_the_copy (#904) and this branch's test_the_materializer_names_a_bad_link_at_the_copy_by_its_own_finding (#1089, N4). Both are kept whole, main's first. adopt-project.py merged by itself. On the merge, tests/test_adopt_root_shape_paths.py gave 122 passed and tests/test_adopt_symlink_target_bytes.py 133 passed. Part of #1089 Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… a bad link at the copy is given a target of its own, or removed, and committed BEFORE `plan` is re-run, and that, kept there with a target of its own, it is then `plan-root-blocks-shape-copy`'s, whose answer is the one that helps; it and `copy_in_the_way` say "names one by one"; the N4 test holds a link (#1089) SHOULD-1 of r1118: the remediation read as if a new target were enough, and left out "commit". Measured: a link at `shape/README.md` with a target of its own is still refused by the materializer; once committed and re-planned with `shape/` kept in the root, `check` names it as `plan-root-blocks-shape-copy`; with `shape/` dropped, sent to a leg, or left to `plan`'s own `spec`, `plan ok`. A fix in the index, not committed, is still `plan-bad-symlink-target`; a fix committed against the old plan is `plan-stale`. NIT-1: "names one by one", as N3 says, in the remediation and in `copy_in_the_way`'s docstring: a 21st empty link at the copy, only counted, is still `plan-root-blocks-shape-copy`'s. NIT-2: the N4 test now sets up the link at `shape/README.md` in both forms a clone holds a late-NUL target `spec\0.md` in: a link to `spec` where git makes links (under `needs_disk_symlinks`), and a file of the blob's 8 bytes where `core.symlinks` is false. It asserts the refusal, that the link or file is as it was, and nothing written where it leads, and pins the remediation from "reports a plan" to "Then run `execute` again" as one run of words. Part of #1089 Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t is one (#1089) NIT-3 of r1118, in tests/test_adopt_symlink_target_bytes.py: "each case is a mutant of them" becomes "each case kills a mutant of them". The paragraph is reflowed; no other word changed. Part of #1089 Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pe writes, the code only, no test rows yet (#1138) Work in progress, stopped at a lane checkpoint. Code only: no test rows, no mutants and no gate yet. - `_KeptTree._at` keeps every row at a path (`every`); `at` is the first row, kept for the root's `.gitattributes` reads and `_links_at_a_pin`. - `files_above` (was `file_above`) returns each FILE twin at the topmost place holding one; `_kept_there` gives a group `("at", row path)` per row at a place, so each twin is its own finding; `_shape_blocks` emits them all; `_links_at_a_copy` reads `group[0] == "at"`. - `_kept_pin_finding` names each row at a pin's file but a named link, and `_holds_a_pin` asks every row. Measured by probe (matrix.py, base 2307036 vs this tree): each change is one added finding naming a twin base left unnamed (the issue's twin after a bad or good link, a twin before a good link, the Makefile copy, a file twin above the copy, the manifest, a pin's file, a submodule twin); no base finding is lost in its first 300 characters. Full-line comparison not done. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… case 1's twin before it (#1138) `a-case-twin-after-the-readme-copys-link` keeps `shape/readme.md` beside a named link at `shape/README.md`. At #1118's head 2307036 it fails "the second finding is lost": `_KeptTree` kept the first row at a place, the link, so the twin was never asked. At dfacd5d it passes. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…link git makes at the copy, above the copy, at the manifest and at a pin's file (#1138) One row per caller of `_KeptTree`'s first row (lane 1's scope ruling): the copy's twin beside a link git makes, before and after it; a link twin at the copy, named once by the copy's finding; a file after a named link, or a second file, above the copy and above `scripts/`; a file after a submodule at the copy; the manifest's twins, a named link after a file twin keeping its tail; a file after a named link at a pin's file, two file twins there, and a file after a submodule there, whose copy under `shape/` is not asked again. At #1118's head 2307036, 16 of the 20 rows of these tests fail, each for the twin it does not name; the 4 that pass are #1118's three cases and the lone link twin. At dfacd5d all 20 pass. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ves as an equivalent mutant and the twin-after case kills M14 too, as measured at head (#1138) #1138 gives each case twin at a place a group of its own, `("at", its own path)`, so the copy skip's marking the link's group seen hides no twin. Measured at 326daf9 against the whole of tests/test_adopt_symlink_target_bytes.py, without -x, in a scratch copy: M12 SURVIVED (150 passed), M13p KILLED by the declared-pin row alone, M14 KILLED by 3 rows (the twin-after row, the Makefile row and the manifest's a-file-twin-after-a-named-link). The comment still said the first case kills M12. Comment only; no code or row changed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideData structuresMake
ImplementationUse the all-row view to report every non-named-link row at a pin’s file, while preserving the existing disk-selection and directory-row behavior.
Emit shape-block findings for every file twin above a destination and every row at the copy or manifest, and update link and pin helpers to recognize the new per-row groups.
TestsAdd coverage for case twins beside links, above shape destinations, at the manifest and at pin files, including submodules and the findings that should remain uniquely attributed to links.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-1, 2026-10-22T21:21:44Z) Independent review r1170 of #1170 at
The code does what the body says: the matrix was re-derived, 13 of 14 of the reviewer's mutants are killed, and M12 is shown equivalent. Lane 1 runs the fix round in a later window. r1170 review of PR #1170 (head Verdict: NOT READY (no MUST so far; three SHOULDs)The code does what the body says, as far as I measured. The three SHOULDs are about the body: two gate rulings are wrong, and the third known limit is narrower than what the code does. What I re-derivedMatrix. I wrote my own harness (
The 20 rows. The head test file run against base Whole file at head. 3.14-like shim ( M12 is an equivalent mutant.
My own mutants (
The one survivor is M12, which is equivalent. Body facts that check out:
FindingsSHOULD-1: the ruling that 29
SHOULD-2: "
SHOULD-3: known limit 3 is narrower than what the code does, and the title's "each case twin" overclaims for a pin's file. (
NIT-1: The other two known limits are honest, measured with
Not covered (stopped at LAST STEP)
Everything is in Lane: openreposhape-1 (openRepoShape-1) |
… with any row at its exact spelling leaves the twins at other spellings unasked, from the disk order of #524, and a submodule there is no row (#1138) r1170 SHOULD-3: the disk that ignores case is asked only where the one that keeps case found nothing, so a file, a link or a directory's rows at the pin's exact spelling end the question. Measured at base and at head alike (`x-pin-exact-file-twin-file`, `x-pin-exact-goodlink-twin-file`, `y-pin-exact-dir-twin-file`: one finding each, the twin never named), and a submodule at the exact spelling does not end it (`y-pin-exact-submodule-twin-file`: the twin is named). Docstring only: `ast.unparse` with docstrings stripped is equal to 4df784a's. Lane 1's ruling is option (b): no code change. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
check names each case twin at a place the shape writes, before a link there or after it, because _KeptTree keeps every row at a path and not the first, so shape/readme.md beside a link at shape/README.md is named (#1138)check names each case twin at the shape's copy, above it and at the manifest, before a link there or after it, and at a pin's file where no row is at its exact spelling, as _KeptTree keeps every row, not the first (#1138)
|
(lane openRepoShape-1, 2026-10-23T08:01:58Z) Independent re-review r1170b of #1170 at r1170b: targeted re-review of PR #1170 at All three of r1170's SHOULDs are fixed as claimed. The code is unchanged since The three SHOULDsSHOULD-1 (CI full-suite fact): fixed.
SHOULD-2 (hygiene over the merge with main): fixed.
SHOULD-3, option (b): fixed.
Limit 3's probes, base
|
| Probe | Base | Head | Twin named at head? |
|---|---|---|---|
x-pin-exact-file-twin-file |
1 | 1 | no |
x-pin-exact-goodlink-twin-file |
1 | 1 | no |
y-pin-exact-dir-twin-file |
1 | 1 | no |
pin-exact-bad-link-twin |
1 | 1 | no |
y-pin-exact-submodule-twin-file |
2 | 2 | yes |
pin-bad-link-then-file |
1 | 2 | yes |
x-pin-file-then-good-link |
1 | 2 | yes |
x-copy-exact-file-twin-file |
1 | 2 | yes |
- Every row matches the body.
- In all 11 cases (3 are my extras), stdout is equal at base and head once masked.
Title, closing form and stacking
- Title: 243 characters, under the 250 limit.
- Closing form: exactly one,
Closes #1138on line 2. No other close, fix, resolve or covers form stands before an issue number. adopt-project.py: a case twin that sorts after a link at a shape copy is not named while the link sits there, because _KeptTree._at keeps the first row (r1118 NIT-5; older than #979) #1138 is OPEN. - Ruling and trailer: the ruling of 2026-10-21T10:32:04Z is named, and the Lane line and the robot line end the body.
- Stacking:
- The base is
fix/1089-named-link-skip-tests, whose head is2307036a(bothgh pr view 1118and the branch API say so). 2307036ais an ancestor of the head, with 5 commits between.- numstat is
124 63 adopt-project.pyand276 10 tests/test_adopt_symlink_target_bytes.py, as the body says.
- The base is
Findings
NIT-1: the title still overclaims for a directory at a pin's exact spelling. (PR title)
- The title says the twin is named "at a pin's file where no row is at its exact spelling". In
y-pin-exact-dir-twin-file, no row is ATcontracts/shape-pin.yaml. The finding's own words are "the filecontracts/shape-pin.yaml/x… insidecontracts/shape-pin.yaml". - Yet
contracts/Shape-pin.yamlis never named: 1 finding at head. - The body and the docstring do state this case.
- Fix: change "where no row is at its exact spelling" to "whose exact spelling holds nothing" (240 characters). That form understates the submodule case, which is safe.
- The same class of overclaim: "each case twin at the shape's copy" is bounded by limit 1 for a directory twin. r1170 accepted that.
NIT-2: two phrases in the new docstring are loose.
adopt-project.py:765: "None where it keeps nothing there but a submodule" reads as "only a submodule". The code also returns None where nothing at all is there (:781-782).adopt-project.py:774: "A submodule there is no row" contradicts_KeptTree.everyat:7135: "Every kept row ATpath, a file, symlink or submodule".- Fix (docstring only, optional): "None where it keeps nothing there, or only a submodule", and "a submodule there ends nothing".
NIT-3: the body's "(the run on 24ee133a is not read here)" is now stale.
- CI run 39859853515 at
24ee133apassed:testsgivesSuccessfully set up CPython (3.14.8)and19672 passed, 385 skipped in 5206.01s.tests-git-bash,packageand SonarCloud all passed. - Fix: say that CI's full
testsjob passed at24ee133atoo.
NIT-4: limit 1 names only _kept_there; the pin's file has the same directory-twin rule.
- In
z-pin-other-dir-twin-file(contracts/SHAPE-pin.yaml/xbeside the filecontracts/Shape-pin.yaml), base and head each give 1 finding, and the directory is never named. - It is the same at base, through
_kept_pin_finding'sat or disk.under(...). - Fix (optional): add "as
_kept_pin_findingdoes at a pin's file" to limit 1.
r1170's NIT-1 still applies: closingIssuesReferences is [] while the base is not main. Check it once the PR is retargeted after #1118 lands.
Not covered (by design: targeted re-review)
- No mutant re-runs. The code's
astis equal to4df784ac's. - No local re-run of gate calls A to E or the four
test_shape_doctor.pyshards. CI's full suite at24ee133acovers them. - No 3.14 shim run. The fix adds no path check.
Everything is in /tmp/claude-1000/-workspace-projects-openRepoShape/300f8f46-004a-4e72-8587-4f192be2e108/scratchpad/r1170b:
tools/limit3_b.pyandastcmp.py;probes-base/andprobes-head/;logs/hyg-merged-24e.log,logs/ci-39828602874.logandlogs/ci-39859853515.log;mclone, the merge-probe clone;cbandch, archives of base and head.
The review checkout /workspace/projects/openRepoShape-worktrees/l1-r1170 is at 24ee133a and clean (status --short shows 0 lines).
Lane: openreposhape-1 (openRepoShape-1)
check names each case twin at the shape's copy, above it and at the manifest, before a link there or after it, and at a pin's file where no row is at its exact spelling, as _KeptTree keeps every row, not the first (#1138)check names each case twin at the shape's copy, above it and at the manifest, before a link there or after it, and at a pin's file whose exact spelling holds nothing, as _KeptTree keeps every row, not the first (#1138)
S = 9ec0dc7, #1118's squash on main. The branch's side was taken for #1118's own files (git diff --name-only S^ S): adopt-project.py, scripts/shape_materialize.py, tests/test_adopt_root_shape_paths.py and tests/test_adopt_symlink_target_bytes.py. The merged tree is 9ce681b, the same as the first merge's, since S's tree equals #1118's final head f4eef87's tree (9737190). Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-1, 2026-11-08T18:05:50Z) Lane 1's delta check of #1170 after #1118 landed: the restack to
Lane: openreposhape-1 (openRepoShape-1) |
|
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 6 days and 17 hours by commenting |



Closes #1138
Was stacked on #1118 (branch
fix/1089-named-link-skip-tests, head2307036a). #1118 has landed, as the squash9ec0dc79onmain, from its final headf4eef876(update-branch's merge ofmain46fe6ac1into2307036a). This branch now holds that squash andmainat9729315f, by three merges and no rebase (see "Restacked after #1118 landed" below). Outside v1.2.0 by Brett Heap's ruling of 2026-10-21T10:32:04Z ("leave the follow-ups out of v1.2.0"): this PR lands after the v1.2.0 tag, on a later word.What
r1118 found, as NIT-5, that a case twin that sorts AFTER a link at a shape copy was never named while the link stayed.
_KeptTree._atkept the first row at a path, so a place was one group and its first row in tree order stood for it. On a disk that ignores case,shape/README.mdandshape/readme.mdare one place, and which of them a clone holds is git's checkout, not the plan's. The plan keeps both, and both are in the way.Against #1118's head the diff is
adopt-project.py(+124, -63) andtests/test_adopt_symlink_target_bytes.py(+276, -10), in 5 commits: the code, a first row, the other rows, a comment-only reword of the tests, and a docstring-only reword of_kept_pin_finding(r1170's SHOULD-3). Every caller of the first-row read is changed in this one PR, each with a row, by lane 1's scope ruling._KeptTreeholds a list of rows per key.every(path)answers all of them in tree order,at(path)the first (what it always answered, for the two readers of ONE row: the root's.gitattributes, whose bytes are one file's, and_links_at_a_pin, whose walks meet one twin). A disk that keeps case has one row at a path at most, so it answers as before.file_aboveis nowfiles_above, a list._kept_thereanswers a list of(group, rows): for EACH row at the place the group("at", that row's own path). A twin is a thing in the way of its own, so it is named by its own finding, and the skip for a named link hides the link's group and no twin's._shape_blocksgives a block for each rowfiles_aboveand_kept_thereanswer: above the shape's directory, abovescripts/, at the copy and at the manifest._kept_pin_findingnames each row at a pin's file that is not a named link, where it was asked of one row. A named link among them is left out and no twin with it. Rows under a directory there stay the first only. The disk that ignores case is asked only where the disk that keeps case found nothing (adopt-project.pycheck: refuse a kept file at the shape's pin path, and an unwritable--work-dir, before any leg exists #524, unchanged), so a pin's file with any row at its exact spelling leaves the twins at other spellings unasked: the third known limit._links_at_a_copy,_links_at_the_manifest,_holds_a_pinread the groups and rows the same way._links_at_a_copyaskedgroup == ("at", copy), which a twin spelled otherwise at the copy's place no longer meets, so it asksgroup[0] == "at"._KeptTreeclass,_kept_pin_findings,_root_shape_findings,_links_at_a_copy,_links_at_the_manifestand_holds_a_pinsay it,_kept_pin_finding's also says when the disk that ignores case is asked, and the comment above_KeptTreenames every case twin as in the way.What
checksays, base against headThe matrix is 30 sources and kept-in-root sets, each run through
checkat base (2307036a) and at head, with each run's own path and each hash of 12 to 40 hex digits masked. 29 ran;dir-twin-after-good-linkis refused byplanat both ("no plan entry for shape/"), as the matrix builds it.Project.yaml, after the filePROJECT.yaml, has the manifest tail on itsplan-bad-symlink-targetline at head ("The plan keeps it in the assembly root, atproject.yamlonce case is ignored, ... (plan-root-blocks-manifest, not said again here): a target of its own would not end that, ..."). At base it lacked it. The finding count stays at 2.p4-after-badshape/readme.mdafter a bad link atshape/README.md,shape/kept (the issue)plan-root-blocks-shape-copyp4-after-goodplan-root-blocks-shape-copyp5-before-goodShape/README.mdbefore a good link atshape/README.mdplan-root-blocks-shape-copymk-after-bad,mk-after-goodshape/makefileafter a bad, or a good, link atshape/Makefileplan-root-blocks-shape-copyeachlk-twin-after-file-danglingshape/readme.md, after the fileshape/README.mdplan-root-blocks-shape-copylk-twin-after-good-danglingplan-root-blocks-shape-copyfa-link-then-fileSHAPE, then the fileShape, above the copyplan-root-blocks-shape-copyfa-file-then-fileSHAPEandShapeabove the copyplan-root-blocks-shape-copyfa-scripts-link-then-fileSCRIPTS, then the fileScripts, abovescripts/plan-root-blocks-shapegl-then-fileshape/README.md, then the fileshape/readme.mdplan-root-blocks-shape-copymf-good-link-twin-fileproject.yaml, and the fileProject.yamlplan-root-blocks-manifestmf-bad-link-then-filePROJECT.yaml, then the fileProject.yaml(tree order)plan-root-blocks-manifestpin-bad-link-then-filecontracts/SHAPE-pin.yaml, then the filecontracts/Shape-pin.yamlplan-root-holds-pinmf-file-then-bad-linkPROJECT.yaml, then a bad linkProject.yaml(tree order)The 14 unchanged:
dir-twins,fa-exact-file,fa-file-alone,ga-twins,lk-alone-dangling,lk-twin-alone-dangling,mf-bad-link-twin-file,mf-file-alone,p4-after-nolink,p5-before-bad(#1118's case 1),p5-before-nolink,pin-exact-bad-link-twin,pin-file-aloneandpin-file-then-bad-link.Why
checkpasses a symlink target of 1024 to 4095 bytes that macOS and XFS cannot make, and a bad link atshape/README.mdorcontracts/shape-pin.yamlis also named byplan-root-blocks-shape-copyorplan-root-holds-pinwith a remedy that cannot help it (follow-ups of #908) #979 is left out by its group, and with a group per row the skip's reach is exactly the link's. Marking the link's group seen, as r1070's M12 does, now hides no twin, so M12 is an equivalent mutant.at()stays, for the readers of one row..gitattributesis read through one file's bytes, and a walk meets one row at a pin's file; a second twin there is never a walk's to name again._at,at,file_aboveand_kept_therethat read the first row only is changed, and each has a row in the tests below.Tests
The runs used a stub
ghfirst on PATH,GH_TOKEN=bogus-w1138, an emptyGH_CONFIG_DIR,GIT_CONFIG_GLOBAL=/dev/null,GIT_SSH_COMMAND=falseandEMAIL, underenv -u LANES_LANE, with one basetemp each and-o tmp_path_retention_policy=failed.The rows: 17 new, 3 of #1118's beside them
tests/test_adopt_symlink_target_bytes.pygoes from 133 tests to 150. The 20 rows of 9 test functions, base (2307036a, given head's test file) against head326daf9a:test_a_named_link_leaves_out_its_own_finding_and_no_othera-case-twin-after-the-readme-copys-linktest_a_case_twin_beside_a_link_git_makes_at_the_copy_is_named_tooa-twin-before-the-link,a-twin-after-the-linktest_a_link_twin_at_the_copy_is_named_once_by_the_copys_findinga-link-twin-alone(control, passes at base),a-link-twin-after-a-file,a-link-twin-after-a-linktest_each_file_twin_above_a_place_the_shape_writes_is_namedSHAPE/Shape) and abovescripts/(SCRIPTS/Scripts)test_a_case_twin_after_a_submodule_at_the_copy_is_namedtest_each_case_twin_at_the_manifest_is_nameda-file-twin-after-a-link-git-makes,a-file-twin-after-a-named-link,a-named-link-after-a-file-twin(asserts the manifest tail)test_a_case_twin_after_a_named_link_at_a_pin_file_is_namedcheckleveltest_each_file_twin_at_a_pin_file_is_named_pin_findingstest_a_file_twin_after_a_submodule_at_the_pin_holds_the_pin_pin_findingsnamescontracts/Shape-pin.yaml, and_root_shape_findings == [](for_holds_a_pin)2307036aa-link-twin-alone326daf9a4df784ac(a comment-only commit after it)24ee133a(a docstring-only commit after that)Each of the 16 failures is for the twin the row names, or for the missing manifest tail; the pin-submodule row finds nothing at base. No row asserts text that depends on the Python version.
Mutants
Each was written into a scratch
git archivecopy, restored in afinallyand proved by sha256 (eddc09f41bd0e955each time, which isadopt-project.pyat both326daf9aand4df784ac: the one commit between them is comment-only, in the test file). M1, M12, M13p and M14 ran against the whole oftests/test_adopt_symlink_target_bytes.py(150 tests) without-x, at326daf9a. M2 to M11 and M7b ran at4df784acagainst the test function written for each, which is the first kill that matters (a mutant that fails a row is killed): each failure is anAssertionErrorat the row, not an import or a syntax error. None of the 12 passed its function, so none needed the whole-file run to prove it survives.adopt-project.pyat the head24ee133ahas the sha256cbc42517033907c0: only_kept_pin_finding's docstring differs from4df784ac's.ast.unparseof the two files with every docstring emptied is equal, so no mutant, which changes code, needs a re-run.r1070's mutants, M13 ported onto #1091's
_kept_pin_findingas in #1118:a-case-twin-of-the-readme-copya-file-at-the-shape-pin-after-a-declared-pins-linka-case-twin-after-the-readme-copys-link,a-file-at-the-makefile-copy-after-the-readme-copys-linkanda-file-twin-after-a-named-linkThe comment above
LINK_BESIDE_A_SECOND_PATHsaid the first case kills M12. It now says M12 survives as an equivalent mutant and the first case is a control.This change's own mutants:
_KeptTreekeeps the first row at a place onlyfiles_aboveasks the first twin above onlytest_each_file_twin_above_a_place_the_shape_writes_is_named: a file after a named link and two files, above the copy and abovescripts/files_abovereturns the first file onlytwo-files-above-the-copyandtwo-files-above-the-shapes-scripts(the one-file rows pass, as they should)_kept_thereasks the first row at the place onlya-twin-after-the-linkanda-case-twin-after-the-readme-copys-link(of 6 run: the twin-before rows pass)_kept_theregroups each twin by the place, not its own patha-link-twin-after-a-fileanda-link-twin-after-a-link(the controla-link-twin-alonepasses)_kept_thereanswers nothing where the FIRST row is a submoduletest_a_case_twin_after_a_submodule_at_the_copy_is_named_shape_blocksasks the first file above the shape path (M7), or above the copy (M7b, an extra), onlytwo-files-above-the-shapes-scripts. M7b KILLED by 1 of 4:two-files-above-the-copy. Each of the two rows kills one and not the other_links_at_a_copynames a link only at the copy's exact spellinga-link-twin-alone, the control that passes at base (the twin-after rows hold a file at the exact spelling, so they do not meet it)_kept_pin_findingasks the first row at the pin's file onlytest_a_case_twin_after_a_named_link_at_a_pin_file_is_named_kept_pin_findingkeeps the first row left after the named linkstest_each_file_twin_at_a_pin_file_is_named_holds_a_pinasks the first row at the pin's file onlytest_a_file_twin_after_a_submodule_at_the_pin_holds_the_pinM1's 14 failed rows are:
a-case-twin-after-the-readme-copys-link,a-twin-after-the-link,a-link-twin-after-a-file,a-link-twin-after-a-link, the fourABOVErows,test_a_case_twin_after_a_submodule_at_the_copy_is_named,a-file-twin-after-a-named-link,a-named-link-after-a-file-twin,test_a_case_twin_after_a_named_link_at_a_pin_file_is_named,test_each_file_twin_at_a_pin_file_is_namedandtest_a_file_twin_after_a_submodule_at_the_pin_holds_the_pin.The gate
The gate ran in the worktree at
4df784ac, in the probe environment above, one pytest call per row, one basetemp each, never more than two calls at once.tests/test_adopt_symlink_target_bytes.pytests/test_adopt_root_shape_paths.py,tests/test_adopt_leg_path_collisions.py,tests/test_scaffold_leg_path_collisions.pytest_adopt_git_rm_bytes,test_adopt_make_reads_first,test_adopt_mount_ignored,test_adopt_pins,test_adopt_root_manifest,test_adopt_root_symlinks,test_adopt_shape_pin_and_work_dirtest_adopt_root_symlinksgave 205 passed, 3 skipped (301.0 s), and the other six gave 260 passed (272.9 s)test_adopt_verify,test_adopt_nested_attributes,test_adopt_clone_claimed,test_non_utf8_git_reads,test_reader_refusals_named,test_shape_advisory,test_update_shapetest_repo_hygiene,test_agents_md_codes,test_cli_referencetests/test_shape_doctor.py, 4 shards of 112 of its 448 testsThe gate again, at
24ee133aThe docstring commit, in the same environment, one basetemp per call, never more than two calls at once.
tests/test_adopt_symlink_target_bytes.pytests/test_adopt_root_shape_paths.py,tests/test_adopt_leg_path_collisions.py,tests/test_scaffold_leg_path_collisions.pytests/test_path_guard_hygiene.py, over the merge intoorigin/main(below)tests/test_shape_doctor.py, shard 04df784actests/test_path_guard_hygiene.pyis onorigin/main, not on this branch, so it ran over the merge.git merge-tree --write-tree origin/main 4df784acis clean (tree9d3a2aa8;origin/mainwas961640c6, which the remote still held when it ran), and a commit of that tree in a scratch clone gives 49 passed in 16.19s; itsadopt-project.pyis identical to the head's. At24ee133athe merge is tree652c11eeand gives 49 passed in 21.02s. The diff adds no bare guard toadopt-project.pyeither: none of its added lines holdsis_file,is_dir,.exists(,is_symlinkoros.path.test_adopt_*.pyfiles were left out of the local gate. Calls A to D hold 13 of them. In all, the local gate ran 22 of the 309tests/test_*.pyfiles: those 13,test_scaffold_leg_path_collisions,test_non_utf8_git_reads,test_reader_refusals_named,test_shape_advisory,test_update_shape, the doc trio andtest_shape_doctor. CI covers the rest: its fulltestsjob passed on CPython 3.14 at4df784ac, the head before the docstring commit, and again at24ee133a(run 39859853515: 19672 passed, 385 skipped, read by r1170b).Restacked after #1118 landed
Three merges and no rebase, each pushed and read back.
mainmoved once after #1118's squash, to #1123's squash, before the third merge.f4eef876(update-branch's merge ofmain46fe6ac1into2307036a): cleanca6154989e8dcfb44da0159367ffd0d9ab2578c324ee133a,f4eef8769ce681b71d53cfe112f0b84c61ebbaac15f7363a9ec0dc79c5725ff9d8bc4fcd69de3db272ab97ed, with--no-commitand the branch's side for S's own four files1b3dbfe6b15facf6b1b2496e85f7c9feef9b7b9aca615498,9ec0dc799ce681b71d53cfe112f0b84c61ebbaac15f7363amainat9729315f1db1285fbd566f66e330f4d2732fd420(#1123's squash, for #1061): clean,adopt-project.pymerged by git05fa55e050a20e7b564b03195275e0f41e919a3d1b3dbfe6,9729315fcc39cd8f2c82f2ebf310a289b68c1fb733f4033egit diff --name-only S^ S) areadopt-project.py,scripts/shape_materialize.py,tests/test_adopt_root_shape_paths.pyandtests/test_adopt_symlink_target_bytes.py. Git stopped on one conflict, intests/test_adopt_symlink_target_bytes.py. Each of the four files was written from the branch's blob (git cat-file blob HEAD:<path>) and added.git ls-files -uwas then empty, andgit write-treegave9ce681b7, merge 1's tree, as it must: S's tree equals F's (9737190e), sincemaindid not move between update-branch and the squash.+and-lines ofgit diff -U0 2307036a 24ee133a(the reviewed delta) and ofgit diff -U0 9729315f 05fa55e0(mainagainst the new head) are byte for byte the same: 400 added and 73 removed lines each, sha2561b3dfac35738eef107daa59a4c22f910897fb4f32936fc701605270b97f3d278each. Both diffs touchadopt-project.pyandtests/test_adopt_symlink_target_bytes.pyonly, so no other file differs frommain.tests/test_path_guard_hygiene.pyis now on this branch, from merge 1, so it ran on the head itself (call C below), not over a scratch merge.The gate at
05fa55e0The probe environment above (with
GH_TOKEN=bogus-w1170r), one basetemp per slot, never more than two calls at once.tests/test_adopt_symlink_target_bytes.py(150),tests/test_adopt_root_shape_paths.py(123)tests/test_adopt_check_throwaway_repos.py(152)tests/test_scaffold_leg_path_collisions.py(104),tests/test_path_guard_hygiene.py(121),tests/test_no_auto_maintenance.py(60)test_a_persons_own_global_config_is_still_read_beneath_the_two, which skips by design when the session begins withGIT_CONFIG_GLOBALset, as the probe environment sets ittests/test_agents_md_codes.py(47),tests/test_cli_reference.py(23),tests/test_repo_hygiene.py(101)tests/test_shape_doctor.py, 4 shards of its 449 testsKnown limits
("dir", path)names the rows below a directory at the place, soshape/readme.md/ybesideshape/README.md/xis one finding, and a directory twin beside a row AT the place is not named while the row is:_kept_thereanswers the rows at the place when it finds any, and the directory only when it finds none._kept_pin_findingdoes the same at a pin's file: a directory twin beside the filecontracts/Shape-pin.yamlis not named (r1170b'sz-pin-other-dir-twin-file, the same at base and head)..gitattributestwins are read throughat(), the first row: one file's bytes are read, andga-twinsis unchanged._kept_pin_findingsasks the disk that keeps case first and the one that ignores it only where that found nothing (adopt-project.pycheck: refuse a kept file at the shape's pin path, and an unwritable--work-dir, before any leg exists #524), so a file, a link (onenamed_linksholds, where adopt-project.py:checkpasses a symlink target of 1024 to 4095 bytes that macOS and XFS cannot make, and a bad link atshape/README.mdorcontracts/shape-pin.yamlis also named byplan-root-blocks-shape-copyorplan-root-holds-pinwith a remedy that cannot help it (follow-ups of #908) #979's rule ends the question too, or one it does not) or the rows of a directory at the pin's exact spelling end the question, and a twin at another spelling is not named, as before this PR. Measured at base (2307036a) and at head alike, one finding each and the twin never named:contracts/shape-pin.yamland the filecontracts/Shape-pin.yaml(x-pin-exact-file-twin-file); a good link at the exact spelling and a file twin (x-pin-exact-goodlink-twin-file); a directory at the exact spelling and a file twin (y-pin-exact-dir-twin-file); andpin-exact-bad-link-twinof the matrix. A submodule at the exact spelling does not end it (y-pin-exact-submodule-twin-file: 2 findings at both, the twin's andplan-submodule-root's). A twin of a row at ANOTHER spelling is asked, before the row or after it (pin-bad-link-then-fileadds a finding;x-pin-file-then-good-link: base 1, head 2). At a copy the same shape does get its twin named (x-copy-exact-file-twin-file: base 1, head 2)._kept_pin_finding's docstring says so.Not in this PR
Lane: openreposhape-1 (openRepoShape-1)
🤖 Generated with Claude Code
Summary by Sourcery
Report all kept case-insensitive path twins that can obstruct project adoption while retaining the existing handling for single-row readers.
Bug Fixes:
checkto report every case-insensitive path twin that can block shape copies, manifests, or pin files, including twins beside links and submodules.Enhancements:
Tests: