Repository navigation
adopt-project.py: check's compare for the mount's git rm skips the .gitattributes it leaves and reads its exit code, so the root's failing filter is plan-attributes-reencoded alone, and a refused compare is named in git's words (#1192) - #1253
brettheap wants to merge 6 commits into
Conversation
…e `.gitattributes` it leaves and reads its exit code, so the root's failing filter is `plan-attributes-reencoded` alone, and a refused compare is named in git's words (#1192) The rehearsal's `update-index -q --refresh` over its `TAKEN` index ran the clean filter of every `.gitattributes` it held, the root's own kept one too, which the mount's `git rm` never compares. Under a required clean filter that fails for that file (a side tree `attr.tree` names saying `/.gitattributes filter=w1136` and `*.md text`, in each of the three configuration forms), the refresh died there, exit 128, nothing read that, and `diff-files` listed every file it had not reached, so `check` also printed `plan-file-reencoded` for `specs/001-feature/spec.md`, which the split stores as its blob. Now each `.gitattributes` the mount leaves stays in the index for its rules and is marked `--assume-unchanged`, so the compare asks what `git rm` asks; and the refresh's exit code is read: where git refuses the compare (a filter failing for a file the plan sends to a leg or drops), that is the finding, `COMPARE_REFUSED` in git's words, and no list is read, as the split's side already does for `git add -u`. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…git rm` removes is still compared, and the store's, compare's and copies' refusals come in the groups' order (#1192) Two more rows for the mutants of #1192's change: only the `.gitattributes` the mount leaves are marked unchanged (a CRLF `docs/.gitattributes` sent to the spec leg under `* text eol=lf` is still named), and a filter that fails for every Markdown file gives the split's store refusal, then the mount's compare refusal, then the copies' refusal. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… step that reads the file again, not at its first clone, and its row asks only that `execute` refuses before any leg (#1192) The row that patched `check` out and ran `execute` stopped extracting the spec leg in 47 of 48 runs here, and once extracting the code leg, after the spec leg was pushed: the spec leg's `git checkout -B` trusted the file's stat data that time and never read it. Which step reads the file again is the clock's to decide (`READ_AGAIN`), so `COMPARE_REFUSED` now names `STOPS_AT_A_READ`: extracting the spec leg, extracting the code leg once the spec leg is pushed, or mounting the legs. The row now checks only that `execute`, unpatched, refuses with that finding before any leg. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideImplementationMark only the
Describe the execution steps at which a refused comparison may be read again, and give the compare refusal its finding text and recovery guidance.
TestsAdd regression coverage for failing filters, comparison controls, refusal reporting and execution timing, including nested and non-UTF-8
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-1, 2026-11-08T15:43:14Z) Independent review r1253 of #1253 at Review of PR #1253 (for #1192) at Verdict: NOT READYThere is no MUST. The core change works:
Two SHOULDs hold it:
FindingsSHOULD-1:
|
| Row | Ids | Base | Head |
|---|---|---|---|
side tree's failing filter over *.md text (three forms) |
3 | 3 failed: two findings | 3 passed |
| CRLF spec control (three forms) | 3 | passed | passed |
| text rule alone control (three forms) | 3 | passed | passed |
| compare refused, in git's words (spec, code, drop) | 3 | failed: wording of the stale list | passed |
execute refuses before any leg (spec, code, drop) |
3 | failed: base's wording | passed |
| root not laid out | 1 | failed: "sends 4 files" | passed |
docs/.gitattributes control |
1 | passed | passed |
| groups' order | 1 | failed | passed |
| total | 18 | 11 failed, 7 passed in 25.57s | 18 passed in 27.27s |
Repeat runs at head:
| Run | Result |
|---|---|
| 1 | 18 passed in 27.27s |
| 2 | 18 passed in 27.11s |
| 3 | 18 passed in 25.76s |
| 4 | 18 passed in 26.54s |
| 5 | 18 passed in 27.17s |
| 6 | 18 passed in 28.71s |
| under the 3.14-like pathlib shim | 18 passed in 28.03s |
Mutants: 13 run, 8 killed by the PR's rows
Every restore read back as blob 698ce8e5, equal to HEAD's.
| Mutant | Change | 18 rows | Result |
|---|---|---|---|
| R1 | marked set inverted (only moved rules) | 7 failed | killed |
| R2 | only the root's own .gitattributes marked |
18 passed | survives; my kept probe kills it (2 failed) |
| R3 | taken read only where git refused |
11 failed | killed |
| R4 | not-laid-out branch's test inverted | 1 failed | killed |
| R5 | stop said as STOPS_AT_THE_SPLIT |
8 failed | killed |
| R6 | refusal order store, copy, compare | 1 failed | killed |
| R7 | --skip-worktree instead of --assume-unchanged |
18 passed | survives; equivalent (refresh and diff-files skip both bits) |
| R8 | --no-assume-unchanged |
6 failed | killed |
| R9 | marked paths encoded as strict UTF-8 | 18 passed | survives; my kept probe kills it (traceback) |
| R10 | exit code read, refusal dropped | 8 failed | killed; check gives plan ok, then execute fails after both legs |
| R11 | rules held out of TAKEN, no mark (the alternative the writer rejected) |
18 passed | survives; equivalent here (the checkout writes every rule to the work tree) |
| R12 | refused compare raised as _Unasked |
8 failed | killed |
| R13 | the mark's exit code unread | 18 passed | survives; equivalent (A7: the mark fails only for a path not in the index) |
Merges
- Head with current main: main moved to
dffc4226(ls-remote); it was0d5704b3when I started.merge-tree --write-treeexits 0, treeeade0643.- Main's changes since base touch neither of the PR's files.
- Head with 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 head7d2f651e: exits 0, tree7513c7df, committed in my clone as1c338aaf.- adopt-project.py: under a failing clean filter that a rule names for
.gitattributes,checkalso printsplan-file-reencodedfor a file the split stores as its blob, beside the rightplan-attributes-reencoded(#1136) #1192's section (18 ids) plus adopt-project.py:checkpasses case twins (aandA,d/xandD/x) that the plan keeps, though a clone on a disk that ignores case checks each pair out as one file; measureexecutethere and decide whetherchecknames them (#1033) #1161's section (62 ids):79 passed, 1 skipped in 101.08s. The skip is "needs a disk that ignores case".
- adopt-project.py: under a failing clean filter that a rule names for
Gate and other summary lines (probe environment, git 2.43, CPython 3.12.3)
| Call | Result |
|---|---|
| throwaway file, whole | 169 passed, 1 skipped in 218.44s (skip: the disk that ignores case) |
test_adopt_reencoded_files.py + test_adopt_nested_attributes.py |
126 passed in 182.25s |
test_adopt_printed_sentences.py + test_adopt_single_repository_record.py |
93 passed in 61.57s |
test_scaffold_leg_path_collisions.py |
104 passed in 5.77s |
test_path_guard_hygiene.py + test_no_auto_maintenance.py |
179 passed, 2 skipped in 34.54s (test_no_auto_maintenance.py:222, caused by GIT_CONFIG_GLOBAL=/dev/null) |
| doc trio | 171 passed in 40.36s |
| execute-stop probe | 48 passed in 58.73s |
| kept, kept-own and moved probes | head 2, 1 and 3 passed; base runs restored to 698ce8e5 |
What I did not cover
test_shape_doctor.pyand the full suite (the brief forbids both), CI's git, real CPython 3.14 (only the pathlib shim), Windows and macOS.- The writer's own 10 mutants, which I did not re-run.
- Whether "mounting the legs" is ever the first read in a run that is not patched.
- User configs such as
core.ignoreStator split-index, and a failingfilter.<x>.processfilter.
Two process notes:
- My first head run hung because the command line used a zsh
${(@f)}flag. Lane 1 stopped it, and I re-ran throughrun-ids.sh. - For about 4 seconds, three of my pytest calls ran at once: the moved probe overlapped two gate calls.
Files
- Review worktree:
/workspace/projects/openRepoShape-worktrees/l1-r1253, clean at18c2aaef. - Scratch folder:
/workspace/projects/openRepoShape-worktrees/_lane1-scratch/r1253/- logs:
logs/ - mutants:
mutants.py, with results inlogs/mutants.tsv - git-level probes:
probes/probe-a.sh,probes/probe-b.sh - probe tests, untracked in my clone only:
clone/tests/test_r1253_probe.py,test_r1253_kept.py,test_r1253_kept_own.py,test_r1253_moved.py - merge worktree:
merge-1189/
- logs:
Nothing was pushed or posted.
Lane: openreposhape-1 (openRepoShape-1)
… the steps `execute` can stop at once both legs are pushed, and `STOPS_AT_A_CLONE`'s comment says each leg is extracted and pushed in turn (#1192) r1253's SHOULD-1: between the extractions and the mount, the split's own clone runs the same `git clone` and `git checkout -q -B` an extraction runs. With the filter kept out of both extractions, `execute` stopped there (`REFUSED cloning the source for the split`) in 24 of 24 runs, a step `STOPS_AT_A_READ` left out. It now reads "extracting the spec leg, extracting the code leg once the spec leg is pushed, or, once both legs are pushed, cloning the source for the split or mounting the legs", and its comment no longer says the mount's `git rm` comes right after the extractions. `compare_refused()` follows. r1253's NIT-1: `STOPS_AT_A_CLONE`'s comment said both legs are extracted before either is pushed; `_build_the_legs` extracts and pushes each leg in turn, the spec leg first. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s under `.github/`, whose filter fails, its folder named in UTF-8 or not, is the split's store's refusal alone (#1192) r1253's SHOULD-2: no row covered the marking of a kept nested `.gitattributes`, or a name that is not UTF-8, and two mutants of the marking survived every row: only the root's own `.gitattributes` marked (R2), and the marked paths encoded as strict UTF-8 rather than by `_nul` (R9). The new row keeps `.github/<folder>/.gitattributes`, the root's `.github/*/.gitattributes filter=w1136` and `*.md text` naming the filter for it, with `<folder>` `kept` and `caf` plus the byte 0xE9; and, a third id, the kept file's own rule naming it. Each gives one finding, the split's store's refusal. On 93d2931 each also gave a false `plan-file-reencoded` for `specs/001-feature/spec.md`. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d out, stops at a step the refused compare names, with the legs pushed it names for that step, and at the split's own clone where neither extraction reads the file (#1192) r1253's optional row for SHOULD-1. No row checked `STOPS_AT_A_READ` against `execute`. This one runs `execute` in-process with `_refuse_what_check_finds` patched out, in two forms: the extractions as they run, and as if each trusted the file's stat data (the filter out of the environment while `_extract_leg` runs, as r1253's probe did). It asks for no step in particular, since the clock decides which step reads the file again: only that the step is one the finding names, that the notice says the legs pushed that the finding names for it, and, in the second form, that both legs are pushed. Where no step read the file, `execute` exits 0 and the row is skipped, as the finding warns. Measured here with git 2.43 over 72 runs: as they run, 47 stopped extracting the spec leg and 1 extracting the code leg once the spec leg was pushed; as if trusting, 24 of 24 stopped cloning the source for the split, both legs pushed. Every run passed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-1, 2026-11-08T16:34:08Z) #1253's fix round for r1253 (issuecomment-6723519114) is pushed at head
Lane: openreposhape-1 (openRepoShape-1) |
|
(lane openRepoShape-1, 2026-11-08T17:13:44Z) Independent re-review r1253b of #1253 at Re-review of PR #1253 (for #1192) at Verdict: READYThere is no MUST and no SHOULD. The round fixes every point r1253 raised:
There are two NITs, one about a sentence in the body and one optional strengthening of the row. FindingsNIT-A: "that it can be is read off the code" (Known limits) overstates what the code shows
NIT-B (optional, not needed for CI): the row could force each read instead of waiting for the clock
NIT-1 (r1253): fixed
NIT-2 (r1253): left as a Known limit, which is right
The brief's checks1. SHOULD-1
2. SHOULD-2's row
3. The optional row: flakiness. See the measurements below.
4. NIT-1 and NIT-2: see above. 5. Mutants: 14 of 14 killed (table below). 6-7. Gate and merges: see the tables below. 8. The body
Flakiness measurements (git 2.43, CPython 3.12.3, 32 cores, other lanes' pytest running)The real row, called unchanged 8 times per id (
Forced branches (
No-read emulation (
Mutants: 14 of 14 killed (#1192 section, 27 ids; clone at
|
| Mutant | Change | Section result | New-row ids failed |
|---|---|---|---|
| X1 | spec leg; in place of spec leg, |
11 failed | 3 STOPS as they run |
| X2 | cloning the split |
11 failed | 3 STOPS trusting |
| X3 | mounting the leg repositories |
8 failed | none (exact rows kill it) |
| X4 | code leg "once the spec leg is made" | 8 failed | none (exact rows kill it) |
| X5 | mount before the split's clone | 11 failed | 3 STOPS trusting |
| X6 | comma, not colon, after "again" | 8 failed | none (exact rows kill it) |
| X7 | stop said as STOPS_AT_A_CLONE |
14 failed | all 6 STOPS |
| X8 | comma before "or mounting the legs" | 8 failed | none (exact rows kill it) |
| X9 | rules whose path is not ASCII left unmarked | 1 failed | KEPT not-utf-8 |
| X10 | marked paths encoded with replace |
1 failed (adopt-git-unasked) |
KEPT not-utf-8 |
| X11 | only rules at most one folder deep marked | 3 failed | 3 KEPT |
| R2 (r1253's) | only the root's own .gitattributes marked |
3 failed | 3 KEPT |
| R5 (r1253's) | stop said as STOPS_AT_THE_SPLIT |
14 failed | all 6 STOPS |
| R9 (r1253's) | marked paths encoded as strict UTF-8 | 1 failed (UnicodeEncodeError) |
KEPT not-utf-8 |
Merges
- Head with current
origin/main12122952:merge-tree --write-treeexits 0, tree2de6f2fc, committed in clone2 ascb1c4e5f.- Unlike at r1253's review, main now changes
adopt-project.py(5926ccca,12122952). The merge is still clean. - Throwaway file on the merge:
178 passed, 1 skipped in 294.99s.
- Head with 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 head:- The branch moved during this review, from
7d2f651eto47478d51("Merge main into adopt-project.py:checkpasses case twins (aandA,d/xandD/x) that the plan keeps, though a clone on a disk that ignores case checks each pair out as one file; measureexecutethere and decide whetherchecknames them (#1033) #1161's branch"). - With
47478d51: exits 0, tree92d3b476, committed asc434aebd. Throwaway file on the merge:221 passed, 2 skipped in 329.76s. Both skips are "needs a disk that ignores case". - With
7d2f651e: exits 0, tree77191828.
- The branch moved during this review, from
Summary lines (probe environment, git 2.43, CPython 3.12.3, at most 2 calls of mine at once)
| Call | Result |
|---|---|
| #1192 section (27 ids) | 27 passed in 49.67s |
| throwaway file, whole | 178 passed, 1 skipped in 246.01s (:493, disk that ignores case) |
test_adopt_reencoded_files.py + test_adopt_nested_attributes.py |
126 passed in 223.60s |
test_adopt_printed_sentences.py + test_adopt_single_repository_record.py |
93 passed in 79.28s |
test_scaffold_leg_path_collisions.py |
104 passed in 6.26s |
test_path_guard_hygiene.py + test_no_auto_maintenance.py |
179 passed, 2 skipped in 38.04s (test_no_auto_maintenance.py:222, caused by GIT_CONFIG_GLOBAL=/dev/null) |
| doc trio | 171 passed in 47.12s |
| section under the 3.14-like pathlib shim | 27 passed in 65.53s |
9 new ids, base 93d29314 (blob 5e8cfd75) |
9 failed in 31.41s |
9 new ids, 18c2aaef (blob 698ce8e5) |
3 failed, 6 passed in 26.77s (the trusting ids) |
| 9 new ids, head | 9 passed in 21.77s |
| repeats A / B / C | 48 / 48 / 48 passed |
| forced branches | real row 30 of 30 passed |
| no-read emulation | 6 skipped in 42.18s |
| merge with main | 178 passed, 1 skipped in 294.99s |
merge with #1189 47478d51 |
221 passed, 2 skipped in 329.76s |
What I did not cover
tests/test_shape_doctor.pyand the full suite (the brief forbids both), CI's own git build, real CPython 3.14 (only the pathlib shim), Windows and macOS.- The writer's T1-T7 and K1, which I did not re-run; I wrote my own X1-X11 instead.
- A file system with whole-second mtimes, or a git built with
USE_NSEC. By reading the code, there the row would always skip, never fail.
Process note: I had two watches running on the #1189 run for a short time. They were read-only, and I stopped the extras.
Files
- Review worktree:
/workspace/projects/openRepoShape-worktrees/l1-r1253b, clean atad70474d. - Scratch folder:
/workspace/projects/openRepoShape-worktrees/_lane1-scratch/r1253b/- logs:
logs/ - mutants:
mutants.py, with results inlogs/mutants.out - repeat records:
logs/repeat-{A,B,C}.rec - branch records:
logs/branches.rec(final),logs/branches-v1.rec - no-read records:
logs/noread.rec
- logs:
- Probe tests, untracked in my clones only:
clone/tests/test_r1253b_branches.py,clone/tests/test_r1253b_noread.py,clone2/tests/test_r1253b_repeat.py. - Merge worktrees:
merge-main/(cb1c4e5f) andmerge-1189/(c434aebd). Both clones' tools are at blob5778d0e3.
Nothing was pushed or posted.
Lane: openreposhape-1 (openRepoShape-1)



Closes #1192
This branch starts from main at
93d29314, #1169's squash, as one more commit series on top of it:e0105903(the change and 16 rows),af48d8de(2 more rows, for the mutants) and18c2aaef(the stop the new finding names, and itsexecuterow; see "Why" for the run that made it change), then the review round's845666aa,898e9a5eandad70474d(see "Review round"). The 18 rows, mutants M1-M10 and the gate under "Tests" ran at18c2aaef; the review round's rows, mutants and gate ran atad70474d.Outside v1.2.0 by Brett Heap's ruling of 2026-10-21T10:32:04Z
What
check's rehearsal asks the mount'sgit rmas a content compare:update-index -q --refreshover itsTAKENindex, thendiff-files --name-only. That index holds what the plan sends to a leg or drops, and also every.gitattributesof the tree, the root's own kept one included, so the refresh ran the clean filter of rulesgit rmnever compares; and nothing read its exit code. Under a required clean filter that fails for the root's.gitattributes, the refresh died there anddiff-fileslisted every file it had not reached. Measured on base93d29314with git 2.43, in-process, wrapping_SplitRehearsal.git, in each of the three forms:update-index -q --refresh: exit 128,fatal: .gitattributes: clean filter 'w1136' failed;diff-files --name-only -z: exit 0,.gitattributes specs/001-feature/spec.md;_reencoded_files_findings: oneplan-file-reencoded.At head (measured at
e0105903, whose_reencoded_files_findingsis18c2aaef's: the later commits change one constant the finding names, comments and rows), the same probe:update-index -z --assume-unchanged --stdinexit 0, the refresh exit 0,diff-fileslists nothing, and_reencoded_files_findingsgives nothing.Measured with
check, git 2.43, CPython 3.12.3; "base" is93d29314, "head" is18c2aaef. "The filter" isfilter.w1136withclean=false,smudge=cat,required=true. "The three forms" are a global config,GIT_CONFIG_PARAMETERSandGIT_CONFIG_COUNT.*.png binary;attr.treenames, by its object id, a side-branch tree whose.gitattributessays/.gitattributes filter=w1136and*.md text; the filter; in each of the three formsplan-attributes-reencoded(clean filter 'w1136' failed) andplan-file-reencoded"the plan sends 1 file to a leg or drops it ...specs/001-feature/spec.md", a file the split stores as its blobplan-attributes-reencodedalonespecs/001-feature/spec.mdcommitted with CRLF (control: the compare runs to its end and a real file is named)plan-attributes-reencodedandplan-file-reencodednaming the spec*.md textalone, no filter (control)plan ok/notes.txt filter=w1136,notes.txtsent to the spec leg, the code leg or dropped; the filter inGIT_CONFIG_PARAMETERSplan-file-reencoded"the plan sends 1 file ... would store it as other bytes than its blob ...:notes.txt", from the list of files the dead compare never reachedplan-file-reencoded: "this machine's git refuses to compare what the plan sends to a leg or drops with its blob, as the mount'sgit rmdoes (git saidfatal: notes.txt: clean filter 'w1136' failed), soexecutewould stop after the leg repositories exist, at the first step that reads that file again: extracting the spec leg, extracting the code leg once the spec leg is pushed, or, once both legs are pushed, cloning the source for the split or mounting the legs. Change the rule or the setting git names, and re-runplan." (head atad70474d)executeREFUSED plan-file-reencodedbefore any leg, with base's wordingREFUSED plan-file-reencodedbefore any leg, with head'sexecutewithcheck's refusal patched out (the measurement behind the stop it names)REFUSED extracting the spec legatgit checkout -q -B adopt/three-repo-shape <commit>,fatal: notes.txt: clean filter 'w1136' failed, in 47 of 48 runs here, wherever the plan sent the file; onceREFUSED extracting the code leg, the same way, "and the spec leg is pushed"; with the filter kept out of both extractions,REFUSED cloning the source for the splitat the samegit checkout -q -B, "and both legs are pushed", in 24 of 24 (r1253) and 24 of 24 hereexecuteis not changedshape, whereshape/.gitattributesgoes, and says* text eol=lfand/notes.txt filter=w1136;notes.txtsent to the spec leg (the rehearsal cannot write the split's root)plan-root-blocks-shape-copyand "the plan sends 4 files to a leg or drops them", three of them LF files the compare never reachedplan-root-blocks-shape-copyand the compare's refusal*.md filter=w1136(every Markdown file)plan-file-reencodednaming the spec from the stale list, then the store's refusal (README.md), then the copies' (AGENTS-shape.md)docs/.gitattributessent to the spec leg under the root's* text eol=lf(control: only the rulesgit rmleaves are skipped)plan-file-reencodednamingdocs/.gitattributesWhy
git rmasks. The mount'sgit rm -r -q -- <paths>compares only what it removes. Each.gitattributesit leaves now stays inTAKEN, so the rules are read as before, and is marked--assume-unchanged, whichupdate-index --refreshanddiff-filesboth skip. Holding them out of the index instead would leave git only the work tree's copies to read the rules from, where today it falls back to the index's; the bit keeps the index as it was and changes only the compare. Their compare result was never used (takenkeeps only paths inmoved), so the bit changes only that their own filter no longer stops the compare. A.gitattributesthe plan sends to a leg or drops, which a rule reaches, is inmovedand still compared (thedocs/.gitattributescontrol; mutant M2).-qmakes a file that needs an update no error, so a nonzero exit is git refusing the compare (a filter that fails on the way in). Then its list is not read, and the finding is git's last line, as_refused_findinggives every refusal of the rehearsal;COMPARE_REFUSEDis the step. This mirrors the split's side, where a refusedgit add -ualready givesstored = []andSTORE_REFUSED. It is also why the brief's plainest reading, "read the code and name nothing on a failure", was not taken: for a filter that fails for a file the plan sends to a leg (thenotes.txtrows), base refused with the stale list,executewith the check patched out made both legs and stopped extracting one, and naming nothing would have made it a falseplan ok..gitattributesis adopt-project.py: a nested.gitattributessettingeolor-textfor a pinned copy outranks #471's root LF block, so Windows writes CRLF #530's D5(a) question (_stored_as_written,plan-attributes-reencoded), and the split's side already leaves it out ofSTOREDfor that reason; a kept nested.gitattributeswhose filter fails is named by the split'sgit add -u(STORE_REFUSED; the review round's row, a folder named in UTF-8 or not, and mutants R2 and R9). So skipping them in the compare loses no finding.executewith the check patched out stopped extracting the spec leg in 47 of 48 runs here and once extracting the code leg, after the spec leg was pushed: that time the spec leg'sgit checkout -Btrusted the file's stat data. Which step reads the file again is the clock's to decide (READ_AGAIN), soSTOPS_AT_A_READnames the steps in order, and that row now asks only thatexecuterefuses before any leg. Each leg's extraction, and then the split's own clone once both legs are pushed (cloning the source for the split), clones the whole source and runsgit checkout -q -Bin it; the mount'sgit rmcomes after all three. The first round of this text left the split's clone out (r1253's SHOULD-1): with the filter kept out of both extractions,executestopped there in 24 of 24 runs. So the text names four steps: extracting the spec leg, extracting the code leg once the spec leg is pushed, or, once both legs are pushed, cloning the source for the split or mounting the legs; and a row now runsexecutewith the check patched out and asks that it stops at one of them, none in particular._reencoded_files_findingsand_SplitRehearsal, each git command goes throughasked(raises_Unasked) orrefused(returns git's line):refused_paths,_rules_reaching'sread-tree,update-index,hash-objectandcheck-attr,hold,checkout,diff-files,add -u,staged, and_copies_stored'sadd -fandhash-object;_throwaway_madeand_objects_borrowedcheck theirs. The refresh was the one rawasked.git(...)call; none is left outside the class.Tests
The 18 new rows (
tests/test_adopt_check_throwaway_repos.py, section "#1192: the mount's compare, under a filter that fails"), base against head, in the probe environment, node ids passed one per argument:93d2931418c2aaeftest_a_side_trees_failing_filter_over_its_text_rule_is_one_finding(the three forms)test_a_file_the_compare_reaches_is_still_named_beside_that_filter(control, three forms)test_the_side_trees_text_rule_alone_is_plan_ok(control, three forms)test_a_compare_git_refuses_is_that_finding_in_gits_words(spec, code, drop)test_execute_refuses_a_compare_git_refuses_before_any_leg(spec, code, drop)test_a_compare_git_refuses_is_named_where_the_root_is_not_laid_outtest_a_gitattributes_the_mount_removes_is_still_compared(control)test_each_refusal_is_named_in_the_groups_orderThe base column ran in a scratch clone at
18c2aaefwithadopt-project.pyreplaced by93d29314's (blob5e8cfd75), and restored to18c2aaef's (blob698ce8e5) afterwards. The 7 that pass on base are the controls, as a control must.Mutants: 10 killed of 10, no survivor. Run in that scratch clone at
18c2aaefover the 18 ids; each mutant replaced one exact text ofadopt-project.py(asserted to occur once), and the file was restored in afinally; after each,git hash-object adopt-project.pywas698ce8e5, equal togit rev-parse HEAD:adopt-project.py.--assume-unchangedcalltest_a_gitattributes_the_mount_removes_is_still_comparedasked.git,compare_said = None)executerows, the no-root row, the order rowdiff-files' list read after a refused comparetest_a_compare_git_refuses_is_named_where_the_root_is_not_laid_outSTOPS_AT_A_CLONEtest_each_refusal_is_named_in_the_groups_order-qdocs/.gitattributescontrol--really-refresh, which ignores the bitThe same ten (M7 then against
STOPS_AT_THE_SPLIT) also killed 10 of 10 ataf48d8de, over that commit's 18 ids.The gate at
18c2aaef, in the worktree, CPython 3.12.3, git 2.43, in the probe environment (a stubghfirst on PATH,GH_TOKEN=bogus-w1192, an emptyGH_CONFIG_DIR,GIT_CONFIG_GLOBAL=/dev/null,GIT_SSH_COMMAND=false,EMAILset to the task's.invalidaddress, noLANES_LANE), each call under 9 minutes, at most two at once:test_adopt_check_throwaway_repos.pytest_adopt_reencoded_files.pytest_adopt_single_repository_record.pytest_scaffold_leg_path_collisions.pytest_path_guard_hygiene.py,test_no_auto_maintenance.pytest_no_auto_maintenance.py:222, "the session began with GIT_CONFIG_GLOBAL set, and its file includes that one", which the probe environment'sGIT_CONFIG_GLOBAL=/dev/nullcausestest_agents_md_codes.py,test_cli_reference.py,test_repo_hygiene.pytest_shape_doctor.py, shard 0 of 4 (113 ids)test_shape_doctor.py, shard 1 of 4 (112 ids)test_shape_doctor.py, shard 2 of 4 (112 ids)test_shape_doctor.py, shard 3 of 4 (112 ids)test_adopt_nested_attributes.py(beyond the brief: it asks D5(a) and the rehearsal)test_adopt_printed_sentences.py(beyond the brief: itsSTOPSrows runexecutewith the check patched out)Earlier rounds of the same calls, before the stop text changed: at
e0105903, G1 167 passed, 1 skipped (183.8 s), G2 51 passed, G3 39 passed; ataf48d8de, G4 104 passed, G5 179 passed, 2 skipped, G6 171 passed, and the four shards 112 passed, 1 skipped; 111 passed, 1 skipped; 112 passed; 111 passed, 1 skipped.Review round (r1253: two SHOULDs, two NITs)
r1253 reviewed
18c2aaef(issuecomment-6723519114): no MUST, two SHOULDs, two NITs. Three commits answer it, each pushed with an explicit refspec and read back withgit ls-remote. Main is not merged here: lane 1 refreshes the branch at landing.845666aa: SHOULD-1 and NIT-1, inSTOPS_AT_A_READand its comment block inadopt-project.py, andcompare_refused()in the test file.898e9a5e: SHOULD-2's row.ad70474d: SHOULD-1's optional row.SHOULD-1: the stop left out "cloning the source for the split". Between the extractions and the mount, the split's own clone runs the same
git cloneandgit checkout -q -Ban extraction runs. With the filter kept out of both extractions, r1253's probe stopped there in 24 of 24 runs.STOPS_AT_A_READnow says "at the first step that reads that file again: extracting the spec leg, extracting the code leg once the spec leg is pushed, or, once both legs are pushed, cloning the source for the split or mounting the legs". Its comment says each extraction, and then the split's clone, runs that checkout, and that the mount'sgit rmcomes after all three.compare_refused(), the "What" table, Why and Known limits follow.test_execute_stops_at_a_step_the_refused_compare_namesrunsexecutein-process with_refuse_what_check_findspatched out, fornotes.txtsent to the spec leg, the code leg or dropped. It has two forms:_extract_legruns, as in r1253's probe.executemust exit 2 at a step the finding names, with git'sfatal: notes.txt: clean filter 'w1136' failed, and its notice must say the legs are pushed that the finding names for that step ("neither leg", "the spec leg", "both legs"). In the second form, both legs must be pushed.executeexits 0 and the row skips, as the finding's own limit saysexecutemay go on. Any other exit fails the row.ad70474d's tool (the row run 8 times per id):SHOULD-2: no row covered a kept nested
.gitattributes, or a name that is not UTF-8.test_a_kept_nested_gitattributes_whose_filter_fails_is_the_stores_alonekeeps.github/<folder>/.gitattributesin the assembly root. Three ids:the-roots-rule: the root says.github/*/.gitattributes filter=w1136and*.md text;<folder>iskept;the-roots-rule-not-utf-8: the same, with<folder>cafand the byte 0xE9.planasks about it as".github/caf\udce9/"withleg: null, answeredroot;its-own-rule, r1253's kept-own variant: the kept file says.gitattributes filter=w1136, and the root says*.md text.codes(result) == [REENCODED_FILES], and that the one finding is the split's store's refusal: the whole line for the UTF-8 names, and every word but the folder's name for the other (NIT-2).93d29314each id gave a second finding, a falseplan-file-reencodednamingspecs/001-feature/spec.md: a second form of adopt-project.py: under a failing clean filter that a rule names for.gitattributes,checkalso printsplan-file-reencodedfor a file the split stores as its blob, beside the rightplan-attributes-reencoded(#1136) #1192, which the change fixed but no row held.skip_where_a_name_cannot_be_not_utf_8), and its folder is built withchr(0xDCE9), so the module imports on Windows.NIT-1:
STOPS_AT_A_CLONE's comment said both legs are extracted "before either is pushed". It now says the refusal comes extracting the spec leg, before either is pushed, since each leg is extracted and then pushed in turn, the spec leg first. The Known limits bullet and the "Not in this PR" line about it are gone.NIT-2: no code change. It is in Known limits as a follow-up candidate, with follow-up text.
The 9 new ids, base against head. They ran in a scratch clone at
ad70474d, withadopt-project.pyswapped to the named commit's. After each run it was restored to blob5778d0e3, equal togit rev-parse HEAD:adopt-project.py.93d29314(blob5e8cfd75)18c2aaef, before this round (blob698ce8e5)ad70474d.gitattributesspecs/001-feature/spec.mdexecutestops, extractions as they runexecutestops, as if trusting the statThe kept row passes at
18c2aaefbecause the marking it holds was already there: it is the coverage r1253 asked for, and R2 and R9 show it holds the marking.Mutants: 11 killed of 11, no survivor. They ran in that scratch clone at
ad70474dover the #1192 section's 27 ids. Each replaced one exact text ofadopt-project.py(asserted to occur once), and the file was restored in afinally. After each,git hash-object adopt-project.pywas5778d0e3, equal togit rev-parse HEAD:adopt-project.py.STOPS_AT_A_READ.18c2aaef.18c2aaef's text: the split's own clone left outexecuteidsexecuteidsexecuteids as they run (code, drop). The spec one passed. Under T5 it can pass only whereexecutestopped at a later step than the spec leg's extraction, one T5 still names, so in that run the clock passed the spec leg by, as it did once in 48 runs above. A row that asks for no step in particular must let that passexecuteids as they runexecuteids.gitattributesmarkedSTOPS_AT_THE_SPLITexecuteids_nulthe-roots-rule-not-utf-8"The exact-text rows" are the 8 ids that hold
compare_refused()whole: the compare refused in git's words (3),executerefuses before any leg (3), the root not laid out (1) and the groups' order (1). Every T mutant fails those 8.The gate at
ad70474d, in the worktree, CPython 3.12.3, git 2.43, in the probe environment (as under "Tests", withGH_TOKEN=bogus-w1253f), each call under 9 minutes, at most two at once:test_adopt_check_throwaway_repos.pytest_adopt_reencoded_files.py,test_adopt_nested_attributes.pytest_adopt_printed_sentences.py,test_adopt_single_repository_record.pytest_scaffold_leg_path_collisions.pytest_path_guard_hygiene.py,test_no_auto_maintenance.pytest_no_auto_maintenance.py:222, caused by the probe environment'sGIT_CONFIG_GLOBAL=/dev/nulltest_shape_doctor.py, shard 0 of 4 (113 ids)Known limits
git add -u.executestops for such a refusal is the clock's (STOPS_AT_A_READ). Measured here with the check patched out: the spec leg's extraction in 47 of 48 runs, the code leg's, after the spec leg was pushed, in one, and again 47 and 1 of 48 atad70474d; with the filter kept out of both extractions, the split's own clone in 24 of 24 (r1253) and 24 of 24 here. The mount'sgit rmwas never the first to read the file in any run: after a checkout that trusted the file's stat data, no later index write falls in the second it was written, unless the clock steps back. In r1253b's probe the mount was the first read only with the index's mtime set by hand (r1253b's NIT-A). In a run where no step reads the file again,executemay go on: the finding names a source that can fail, as the moved-files finding does (READ_AGAIN).update-index --refreshanddiff-filesskip an--assume-unchangedentry is git's documented behaviour; CI's git was not measured here._SplitRehearsal.refuseddecodes git's stderr with"replace", so in the review round's row git's words name the folder ascafand U+FFFD where_spelled's escape (adopt-project.py: a kept or dropped name reaches the split commit and the pull request body unspelled, and_spelled,_shownandplan's report print text that reads as another name, or raw #900) would printcaf\udce9; the row holds every word of that finding but the folder's name. It reaches every refusal the rehearsal names (checkout, store, copy, and now compare). Follow-up text, if wanted: "adopt-project.py:_SplitRehearsal.refuseddecodes git's stderr withreplace, so a path that is not UTF-8 in a refusalchecknames prints as U+FFFD, not as_spelled's\udcNNescape (measured on adopt-project.py:check's compare for the mount'sgit rmskips the.gitattributesit leaves and reads its exit code, so the root's failing filter isplan-attributes-reencodedalone, and a refused compare is named in git's words (#1192) #1253's branch with git 2.43:fatal: .github/caf<U+FFFD>/.gitattributes: clean filter 'w1136' failed)".Not in this PR
_stored_as_written), the split's side (git add -u), the pinned copies' question, orexecute's own commands.TAKENholds, or to which files are asked about.--helptext or doc-trio row: no doc namesplan-file-reencoded's sentences (git grep -n 'file-reencoded' -- ':!tests' ':!adopt-project.py'is empty).Lane: openreposhape-1 (openRepoShape-1)
🤖 Generated with Claude Code