Repository navigation
scaffold-project.py: a partial-clone --pin-source short of the pinned commit or its trees is pin-source-unreadable, named a partial clone with git fetch --refetch --no-filter <remote> <commit>, and no longer fetched into (#1182) - #1223
brettheap wants to merge 11 commits into
Conversation
…e scaffold reads its pins with `GIT_NO_LAZY_FETCH=1`, so a partial-clone `--pin-source` short of the pinned commit or its trees is `pin-source-unreadable`, naming the partial clone and `git fetch --refetch --no-filter`, and is no longer fetched into (#1182) Measured on main 961640c with git 2.43, through the scaffold's real run and `--dry-run` alike: a `--filter=tree:0 --no-checkout` `--pin-source`, and a `--filter=blob:none --no-checkout` one lacking the pinned commit, each ran the clone's promisor `upload-pack` once (`.promisor` 1 -> 2) and the run went on to exit 0. A `blob:none` clone holding the commit ran none. Now `_declaration` reads the pins through `pin_values_unfetched`, which is `declared_pin_values` under `lazy_fetch_off` (the environment put back after), and which re-raises the reader's own `pin-source-unreadable` for a partial clone (`promisor_remotes`, the doctor's #683 rule) with that reason and the `git fetch --refetch --no-filter <remote>` cure. A clone that is no partial one keeps the reader's refusal and its `git fetch --all` remediation. Both forms now exit 2 with no `upload-pack` run and nothing created. `--pin-source`'s help says so; docs/cli.md regenerated with scripts/render-cli-reference.py. `adopt-project.py plan` is not changed here. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the clone only for `pin-source-unreadable`, which only a `--pin-source` gives (#1182) Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…--pin-source`, each counting the clone's `upload-pack` runs: refused with none at head, fetched into once on base (#1182) Each row clones a local origin (`make_source_repo`, `main` plus a `side` commit, filters allowed) and sets `remote.origin.uploadpack` to a wrapper that logs each run, so "no fetch" is an answer the controls give too. - A `tree:0` clone holding the pinned commit and none of its trees, and a `blob:none` clone lacking the commit, `--dry-run` and the real run: `pin-source-unreadable` naming the partial clone, `origin` and `git fetch --refetch --no-filter origin`, no `git fetch --all`, nothing made, 0 runs, `objects/` unchanged. On 961640c: exit 0 after 1 run each. - The cure as printed, run in a `tree:0` clone, lets the real run read the origin's digest with no fetch of its own. - Controls, passing on 961640c too: a full clone, a `blob:none` clone holding the commit (checked out or not) and a `tree:0` clone made whole read the origin's digest with 0 runs; a full clone lacking the commit keeps `git fetch --all`; a folder in a partial clone is still `pin-source-not-a-clone`, with no partial sentence. - In process: `main` refuses a `tree:0` clone with 0 runs, and with `NO_LAZY_FETCH` renamed to a name git does not read, it fetches and exits 0; neither name is left in `os.environ`. - `pin_values_unfetched` with two promisor remotes names both and gives one cure each, in the reader's words, with the reader's refusal as the cause. - `lazy_fetch_off`, `promisor_remotes` (10 configs and a missing folder), `refetch_command` and `_remote_named`, one by one. Rows needing git to honour `GIT_NO_LAZY_FETCH` ask it first and skip where it is ignored. 36 pass at head; on 961640c the 6 controls pass and the other 30 fail. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The clone is named by its resolved absolute path in the refusal's remediation, not as typed; a row with a relative --pin-source kills the mutant that names it as typed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… and raises nothing (#1182) A remote.<name>.promisor of 'maybe' makes git config --type=bool --get-regexp exit 128 with nothing written (git 2.43 collects every value before it writes one), so only the partialclonefilter names a remote. The row passes with or without the returncode check in _config_listed: no config makes git 2.43 write part of a listing and then fail, so that check stays a named survivor. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR changes scaffold pin resolution to disable Git lazy fetching, detect partial-clone sources from configuration, and refuse unreadable sources with safe per-remote refetch instructions while preserving existing full-clone behavior; it also updates CLI documentation and adds extensive focused tests. Sequence diagram for partial-clone pin-source readingsequenceDiagram
participant Scaffold
participant Git
participant Promisor
Scaffold->>Git: declared_pin_values(args)
Note over Scaffold,Git: GIT_NO_LAZY_FETCH=1
Git-->>Scaffold: pin-source-unreadable
Scaffold->>Git: promisor_remotes(clone)
Git-->>Scaffold: promisor remote names
Scaffold-->>Scaffold: Build safe refetch remediation
Scaffold-->>Scaffold: Refuse without creating output
Flow diagram for unreadable partial pin-source remediationflowchart TD
A[Read pin values with lazy fetching disabled] --> B{Pin source readable?}
B -->|Yes| C[Continue scaffold]
B -->|No| D{Partial clone in config?}
D -->|No| E[Preserve pin-source-unreadable refusal]
D -->|Yes| F[Identify promisor remotes]
F --> G[Print git fetch --refetch --no-filter per remote]
G --> H[Refuse without creating output]
File-Level Changes
Assessment against linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-1, 2026-10-23T20:50:54Z) r1223, lane 1's independent reviewer (not the writer), reviewed #1223 at
The full reviewReview of PR #1223 (r1223, first independent review), head
|
| id | mutant | result (first failing row) |
|---|---|---|
| S1 | _declaration reads with declared_pin_values again |
killed (refused[tree0-dry-run]) |
| S2 | reader="the tool" |
killed (refused[tree0-dry-run]) |
| S3 | nothing_made="" |
killed (refused[tree0-dry-run]) |
| S4 | help sentences dropped | killed (test_cli_reference) |
| S5 | declared_pin_values not imported into the scaffold |
killed (test_adopt_pins one-implementation) |
| M1 | variable set to "0" |
killed (refused[tree0-dry-run]) |
| M2 | prior value forgotten | killed (lazy_fetch_off[0]) |
| M3 | an unset variable not removed | killed (in-process) |
| M4 | a prior value removed, not restored | killed (lazy_fetch_off[0]) |
| M5 | restored only on a normal exit | killed (in-process) |
| M6 | name hard-coded, not read from NO_LAZY_FETCH |
killed (in-process) |
| M7 | _config_listed raises OSError |
killed (folder not there) |
| M8 | a failed run's stdout returned (writer's M08) | SURVIVED |
| M9 | -z dropped |
killed (refused) |
| M10 | git config run in the process cwd |
killed (refused) |
| M11 | --type=bool dropped |
killed (promisor-yes) |
| M12 | any promisor value counts | killed (promisor-false) |
| M13 | extensions.partialclone kept in config order |
killed (named-last) |
| M14 | partialclonefilter alone not a promisor |
killed (filter) |
| M15 | or changed to and |
killed (refused) |
| M16 | no dedupe | killed (refused) |
| M17 | the named remote not moved last | killed (named-last) |
| M18 | the named remote first | killed (named-last) |
| M19 | subsection cut at the first dot | killed (dotted-name) |
| M20 | extensions.partialclone dropped from PROMISOR_KEYS |
killed (named-last) |
| M21 | _remote_named ignores backticks |
killed (refetch[a-backtick]) |
| M22 | _remote_named ignores printability |
killed (refetch[unprintable]) |
| M23 | refetch_command pastes an option |
killed (an-option) |
| M24 | refetch_command does not quote |
killed (spaced) |
| M25 | refetch_command ignores backticks |
killed (a-backtick) |
| M26 | no lazy_fetch_off (the fix undone) |
killed (refused) |
| M27 | every refusal code asked about | killed (folder in a partial clone) |
| M28 | clone named as typed (writer's M37) | killed (relative pin source) |
| M29 | a clone with no promisor remote still called partial | killed (full clone lacking the commit) |
| M30 | from None |
killed (two promisor remotes) |
| M31 | only the first remote's cure | killed (two promisor remotes) |
| M32 | only the first remote named | killed (two promisor remotes) |
| M33 | reader hard-coded in the detail | killed (two promisor remotes) |
| M34 | the reader's own detail dropped | killed (two promisor remotes) |
| M35 | nothing_made dropped |
killed (refused) |
| M36 | PIN_SOURCE_UNREADABLE misspelt |
killed (refused) |
M8 is equivalent on git 2.43, as the body says. $S/probe_m08.py ran both listings _config_listed makes over many configs. Not one wrote output and then failed:
- a bad boolean, in all 6 orders, and in local, included, global,
GIT_CONFIG_COUNTandGIT_CONFIG_PARAMETERSscope:bool rc=128 stdout=0B fatal: bad boolean config value 'maybe'; - a bad config line:
rc=128 stdout=0Bfor both listings; - a valueless
extensions.partialclone: git itself segfaults (rc=-11 stdout=0B).
Measurements
- Base against head (new file only). Head:
38 passed, rc 0. Baseef619c74awith the file copied in:32 failed, 6 passed. The 6 are the 4 controls, the full clone lacking the commit and the folder row, all of which pass on both. The refusal, relative-path and cure rows fail on base by behaviour (assert 0 == 2, and no cure found). The unit rows fail withAttributeError, since the helpers are new. - No fetch (
$S/probe_nofetch.py, which countsupload-packruns, packs and.promisorfiles):- Base, every short form (
tree0,lacks-commit,tree0-bare, behind), dry and real run:rc=0 runs 0->1 packs 1->2 promisor 1->2. - Head, the same forms:
rc=2 runs 0->0 packs 1->1 promisor 1->1 made=[]. - Accepted runs (
blobnone,full) on both:rc=0 runs 0->0. - After the cure, a dry run and a real run left
objects/byte-identical. An earlier 13 to 10 file change came from git's background gc after the cure, not from the scaffold.
- Base, every short form (
- 3.14-like shim (
$S/shim/sitecustomize.py; it was shown to load, and turns aPermissionErrorinto False): the new file gives38 passed. - Docs and codes:
scripts/render-cli-reference.py --checkgives[ok] docs/cli.md is what the generator produces.test_path_guard_hygiene,test_agents_md_codes,test_cli_referenceandtest_repo_hygienegive220 passed.test_no_auto_maintenancegives58 passed, 2 skipped(the probe environment'sGIT_CONFIG_GLOBALskip).- The PR reuses
pin-source-unreadableand adds no code. The code test scans onlyAGENTS.md, so nothing new needs listing.
- With main: main is 12 commits past
ef619c74a; 50ce8ab touchesscaffold-project.pyand 0d76e4c touchesshape_materialize.py.git merge-treeis clean (treeda415ced8). On that tree, the new file plus hygiene, cli-reference and codes give229 passed, and the render check is ok. - Body claims checked and true:
- exactly one closing form (
Closes #1182);closingIssuesReferences= [1182]; title is 225 characters; - the stacking statement (merge-base
ef619c74a); - table rows 1, 2 and 4 (stderr byte-identical on base and head), and row 3 (see NIT 4);
- 38 rows: 11
promisor_remotesconfigs, 5refetch_commandnames, 4_remote_namednames; - 53 + 9 = 62 gate files; collect-only gives
5692 tests collected, doctor 448,test_repo_hygiene101; - the totals arithmetic (5687+5; 445+3; 6132+8; 6454+10; 6353 distinct);
- the cited skip reasons;
scaffold.declared_pin_valuesis read bytest_adopt_pins.py:1818andtest_scaffold_leg_path_collisions.py:438;- the promisor rule matches
shape-doctor.pyline for line.
- exactly one closing form (
Not covered
- The body's full gate (6454 passed) was not re-run, as briefed. Only its arithmetic and collected counts were checked.
- A git that ignores
GIT_NO_LAZY_FETCH: the bench has only 2.43.0, which honours it. I did not verify the version-history claims (2.45.0, the 2024-05-14 releases, Ubuntu's build). - Real CPython 3.14: only the shim was used.
- A real GitHub promisor remote: every probe used a
file://origin, including the fetch of a commit by id. - I did not re-run the writer's own mutant batches. My 41, run at the merge head, stand in for them.
- 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 beyond its file list, its helper names and a clean merge-tree with this head.
Lane: openreposhape-1 (openRepoShape-1)
Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t by its id, so a blob:none clone whose branches lack it is made readable too (#1182) The printed cure was `git fetch --refetch --no-filter <remote>`, which refetches only what the clone's branches hold: a `blob:none` clone of `main` pinned on `side` was still `pin-source-unreadable` after it (r1223 on #1223). Now `git fetch --refetch --no-filter <remote> <commit>`, the commit the reader could not read, which `pin_digest_from_source`'s refusal now carries. A row runs the printed cure on that form and re-runs the scaffold to exit 0, as the `tree:0` row does. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nothing into a partial clone on a git that honours GIT_NO_LAZY_FETCH, and that an older git fetches (#1182) "the scaffold never fetches into it" held with no condition, where the PR's own known limits say a git that ignores `GIT_NO_LAZY_FETCH` fetches as before (r1223 on #1223, NIT 1). The sentence is #1190's: on a git that honours it (2.45 or later, or a security release from 2.39.4 on). docs/cli.md regenerated with scripts/render-cli-reference.py. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ch (#1182) The body's table names a checked-out `tree:0` clone (exit 0, no `upload-pack`), and no row held it (r1223 on #1223, NIT 4). It is now a kind, `tree0-checkout`, and a control row beside the full and `blob:none` clones. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
--pin-source short of the pinned commit or its trees is pin-source-unreadable, named a partial clone with git fetch --refetch --no-filter <remote>, and no longer fetched into (#1182)--pin-source short of the pinned commit or its trees is pin-source-unreadable, named a partial clone with git fetch --refetch --no-filter <remote> <commit>, and no longer fetched into (#1182)
|
(lane openRepoShape-1, 2026-10-23T21:57:04Z) r1223b, lane 1's independent re-checker (not the writer), re-checked #1223's fix round at
The full re-checkRe-check of PR #1223 at
|
… not a reviewer's id (#1182) Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-1, 2026-10-23T22:02:54Z) Lane 1's delta check of
Lane: openreposhape-1 (openRepoShape-1) |



Closes #1182
Outside v1.2.0 by Brett Heap's ruling of 2026-10-21T10:32:04Z ("leave the follow-ups out of v1.2.0"). Measured against
ef619c74a, the merge-base before the review round; main ate04826db4is merged in since (5d90b9edb, a clean merge).Review round (r1223: no MUST, one SHOULD, four NITs)
git fetch --refetch --no-filter <remote> <commit>. Without it, ablob:noneclone ofmainpinned on another branch stayedpin-source-unreadableafter the printed cure. A new row runs the printed cure on that form and re-runs the scaffold to exit 0 (70babae7b).--pin-sourcehelp says the scaffold fetches nothing into a partial clone on a git that honoursGIT_NO_LAZY_FETCH, and that an older git fetches;docs/cli.mdis regenerated (4fc047abb).ef619c74a, the PR's merge-base, in the table and in Tests.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 is named as open, adding its own helpers.tree:0clone is a kind and a control row (686a25253).What
scaffold-project.py --pin <product>@<commit> --pin-source <clone>read the pin's digest with git in the clone (git cat-file -tof the commit, thengit ls-tree -r). In a partial clone (git clone --filter) git fetches an object the clone lacks from its promisor remote on any read that needs it. Measured through the scaffold's own command line onef619c74aand at this head (686a25253), git 2.43.0, the real run and--dry-runalike, with r1223'sprobe_nofetch.pyand this PR's rows:--pin-sourceef619c74a--filter=tree:0 --no-checkout(holds the pinned commit, none of its trees)upload-packran 1 time,.promisor1 -> 2pin-source-unreadable, 0 runs,.promisorunchanged, nothing made--filter=blob:none --no-checkoutlacking the pinned commit.promisor1 -> 2pin-source-unreadable, 0 runs, unchanged, nothing madeblob:noneholding the commit, checked out or not; a full clone;tree:0checked outpin-source-unreadablewithgit fetch --allfirstThe refusal for a partial clone keeps the code
pin-source-unreadableand the reader's own words, then says that the clone is a partial clone whose git fetches an object it lacks from its promisor remote (named), that the scaffold never fetches into a--pin-source, and names the cure:git fetch --refetch --no-filter origin <commit>in the clone, the pinned commit the reader could not read (one fetch per promisor remote, named in git's order). The cure as printed makes every short form readable: atree:0clone, checked out or bare, ablob:noneclone that lacks the commit on another branch, and one that is merely behind. The scaffold run again reads the origin's own digest of that commit with 0 runs of its own.The
--pin-sourcehelp gains two sentences, the second in #1190's words: a partial clone is read where it holds the pinned commit and its trees; on a git that honoursGIT_NO_LAZY_FETCH(2.45 or later, or a security release from 2.39.4 on), one git could read only by fetching what it lacks ispin-source-unreadable, and the scaffold fetches nothing into it; an older git fetches what it lacks into it. git v2.45.0's ownDocumentation/git.txtdocuments the variable.docs/cli.mdis regenerated from the help.Why
pin_values_unfetched(args, *, reader, nothing_made)inscripts/shape_materialize.pyisdeclared_pin_values(args)read underlazy_fetch_off(), which setsGIT_NO_LAZY_FETCH=1for every git the read starts and puts the variable back (a prior value restored, an unset one removed). A read of an object the clone lacks then fails where it fetched, and the reader already answers a failed read.pin-source-unreadableIS ASKED ABOUT. A partial clone that holds the commit and its trees reads as any clone does. Whether a clone is a partial one is read from its config alone (promisor_remotes: git 2.43'spromisor-remote.crule, as the doctor's shape-doctor.py: run from a partial clone of the standard, theshape currencyrow fetches what the clone lacks, and with its remote out of reach calls each copy the standard changed since the pinboth, so a doctor that fetches nothing fetches, and offline says DRIFTED of copies nobody edited #683 reader:promisortrue, apartialclonefilter, and the remoteextensions.partialclonenames, last), so nothing is fetched to find out. A clone that is no partial one keeps the reader's refusal as it was.git fetch --refetch --no-filter <remote> <commit>, not the reader'sgit fetch --all: that one keeps the clone's filter, and names no cause for atree:0clone that holds the commit and lacks its trees.--single-branchblob:noneclone ofmain, pinned onside: stillpin-source-unreadableaftergit fetch --refetch --no-filter origin, and aftergit fetch --all. Named by its id, the commit comes with every tree and blob it reaches, from a promisor remote, which serves an object by id because that is how a lazy fetch asks.pin_digest_from_source's refusal now carries the commit it could not read (refusal.commit), so the cure names that commit, the one the refusal's first line names.readerandnothing_madelet one helper speak in either tool's words. The scaffold passesreader="the scaffold"and itsUNREAD_PIN_SOURCE_MADE.adopt-project.pyis NOT changed. 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 (open) adds its own_lazy_fetch_offand_pins_read_unfetched(git merge-treeof this head and 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's359a749cis clean); a follow-up can makeplancallpin_values_unfetched(args, reader="plan", nothing_made=PLAN_NOT_WRITTEN)and drop its copy.declared_pin_valuesstays imported inscaffold-project.py(# noqa: E402, F401), becausetests/test_adopt_pins.pyandtests/test_scaffold_leg_path_collisions.pyreadscaffold.declared_pin_values.Tests
tests/test_scaffold_partial_pin_source.py, 40 rows, standalone (conftest'sgit,run_script,SCAFFOLD,make_source_repo,FILE_PROTOCOL). Each row clones a local origin (file://, no network, nogh) and setsremote.origin.uploadpackto a wrapper that logs each run, so "no fetch" is an answer the controls give too. Rows that need git to honourGIT_NO_LAZY_FETCHask it first and skip, saying why, where it is ignored.git fetch --alland no refetch that names no commit, nothing made, 0 runs,objects/unchanged;--pin-source openGlass(run in the folder holding it): the remediation names the resolved absolute path, notopenGlass;tree:0clone and in ablob:noneclone that lacks the commit (2 rows), lets the real run read the origin's digest of the pinned commit with no fetch of its own;blob:none(checked out or not), a checked-outtree:0clone, and atree:0clone made whole byfetch --refetch --no-filter, read the origin's digest with 0 runs; a full clone lacking the commit keepsgit fetch --all; a folder in a partial clone is stillpin-source-not-a-clone;mainrefuses atree:0clone with 0 runs, and withNO_LAZY_FETCHrenamed to a name git does not read it fetches and exits 0 (so the variable is what stops the fetch); neither name is left inos.environ;lazy_fetch_off(set inside; a prior0restored; an unset one removed; restored when the read raises),promisor_remotes(11 configs, including a boolean git itself dies on, and a folder that is not there),refetch_command(5 names, each with a commit),_remote_named(4 names).Base vs head, the new file copied into a clean clone at
ef619c74a: base 33 failed, 7 passed. The 7 are the 5 controls, the full clone lacking the commit and the folder row, which read the same on base; the 5 controls also pass at961640c6(5 passed), as their docstring says. Both cure rows fail on base atassert printed, because base prints no cure. Head686a25253: 40 passed, none skipped (git 2.43.0 here honours the variable), and 40 passed under a 3.14-likesitecustomizeshim, where pathlib'sis_file,existsandis_diranswer False to anyOSError.Mutants of the new lines, one at a time, each run with the new file (S04 with
tests/test_cli_reference.py), the file restored infinallyand its blob checked equal to HEAD's after each: 42 (M01-M38 inshape_materialize.py, S01-S04 inscaffold-project.py). 41 killed, 1 survivor.--pin-source; the relative-path row kills it (checked ataf859e6a5)._config_listedreturns a failed run's stdout (return listed.stdout) instead of nothing. It is an equivalent mutant against real git: probed with git 2.43.0,git config -z --type=bool --get-regexp '^remote\..*\.promisor$'overremote.a.promisor=true,remote.b.promisor=maybe,remote.c.promisor=true(in each order of the three) exits 128 withfatal: bad boolean config value 'maybe' for 'remote.b.promisor'and writes NOTHING, because git collects every value before it writes one; a bad config line (fatal: bad config line) does the same. No config makes git 2.43 write part of a listing and then fail, so no row with a real git tells the two apart. Abad-booleanrow was added topromisor_remotes' table anyway (a failed listing names nothing and nothing raises); it passes with or without the mutant. A stubgitthat writes a record and exits 128 would kill M08 (precedent:stub_gitintests/test_pinned_copy_text.py); not added, as it tests a git nobody has seen.69fbca91a; M37 alone ataf859e6a5and M08 alone atcb4786860. They were not re-run at the merge commit.77687d685: 40 killed, and M8 survived, the same equivalent mutant.70babae7b, in a clone in the writer's scratch folder, with the file restored and its blob checked equal to HEAD's after each:git fetch --refetch --no-filter <remote>again): killed by 9 rows.test_the_cure_as_printed_makes_the_clone_readable[lacks-commit]kills it by behaviour: the scaffold re-run after the printed cure exits 2 (assert 2 == 0), while its[tree0]twin passes.refetch_commandrows that say it in words.Gate
This round, at
686a25253The same probe environment, with
TMPDIRin the writer's scratch folder; one basetemp per call; worktree clean.tests/test_scaffold_partial_pin_source.pysitecustomizeshim (PYTHONPATH, shown to load)tests/test_adopt_pins.py::test_both_tools_read_a_pin_with_one_implementation,tests/test_scaffold_leg_path_collisions.pytests/test_path_guard_hygiene.py,tests/test_no_auto_maintenance.pytests/test_no_auto_maintenance.py:222, x2, the reason below)tests/test_agents_md_codes.py,tests/test_cli_reference.py,tests/test_repo_hygiene.pyscripts/render-cli-reference.py --check:[ok] docs/cli.md is what the generator producesef619c74aThe 62-file gate below was NOT re-run at this head. It stands at
77687d685, before the merge of maine04826db4(12 commits;scaffold-project.pyandscripts/shape_materialize.pyamong them) and before this round's three commits.The full gate, at
77687d685Probe environment on every call (a stub
ghfirst on PATH, a bogusGH_TOKEN, an emptyGH_CONFIG_DIR,GIT_CONFIG_GLOBAL=/dev/null,GIT_SSH_COMMAND=false,EMAILset,env -u LANES_LANE), one basetemp per call, each call under 530 s, at most 2 at once, at head77687d685(the merge of mainef619c74a).The checkpoint's gate list file was missing. It was rebuilt as the checkpoint's own command (
grep -lE -- "--pin-source|pin_source|declared_pin_values" tests/*.py: 53 files at this head, with the new one) plustests/test_scaffold*.pyandtests/test_setup_project*.py(9 more): 62 files, 5692 tests. A first call of 20 of those files hit the 530 s limit (rc 124 with 27% run; no result), so they were run in 24 units of at most 260 tests.COMPLETE: every call below ended with exit code 0, at
77687d685(clean worktree), from 18:07Z to 18:51Z. The result column is pytest's own summary line, with-rfEsso each skip names its reason. One row per call; f00-f23 are the 24 units of the 62 files, d0-d3 the doctor's 4 shards.tests/test_path_guard_hygiene.py,tests/test_no_auto_maintenance.pytests/test_no_auto_maintenance.py:222, x2: "the session began with GIT_CONFIG_GLOBAL set, and its file includes that one", which the probe environment does)tests/test_agents_md_codes.py,tests/test_cli_reference.py,tests/test_repo_hygiene.py,tests/test_readme_windows_quoting_sentence.pytest_scaffold_device_names,_e2e,_free_text_values,_ignored_shape_filestest_scaffold_init_empty_values,_leg_path_collisions,_leg_pathstest_scaffold_manifest_values,_mount_ignored,_pin_and_reusetest_scaffold_pin_collisions,_referent_chain_links,_reuse_seeded_legs,test_setup_project_dry_run_offerstest_setup_project_name_edgestest_setup_project_pytest_setup_project_ref_parts,_repo_misdrawn,_typed_name,test_adopt_pinstest_chain_pin_sources,test_neutral_pin_validator,test_pin_annotated_tag,test_pin_source_env_collisionstest_pin_source_top_level,test_pin_source_unreadable,test_pinned_copy_texttest_validate_pins_named_and_spelledtest_validate_pins_spelled_and_named,_without_git,conftest.pytest_adopt_entry_paths,test_adopt_execute_refusalstest_adopt_finding_spelling,_ignored_shape_files,_manifest_valuestest_adopt_plan,_plan_escapes,_plan_flag_refusals_firsttests/test_adopt_plan.py:567:SHAPE_MEDXEHR_CLONEis unset, or names no directory: the MedxEHR-backed plan test needs a read-only clone of MedxSoft/MedxEHR)test_adopt_plan_yaml_quoting(one file of 540 tests, run whole: over the 260 bound)test_adopt_question_and_submodule_spellingtest_adopt_root_symlinks,test_adopt_spelled_namestests/test_adopt_root_symlinks.py:1926,:1981,:2532: "the reftable backend needs git 2.45 or later"; this bench has git 2.43.0)test_adopt_template_values,test_assembly_root_unreadable_paths,test_chain_link_fold,test_chain_remedy_callerstest_clone_url_bytes,test_doctor_non_ascii_paths,test_doctor_validator_rowstest_doctor_validator_verdicts,test_dry_run_parity,test_github_name_limits,test_link_source_chain_defaultstest_naming_policytest_non_utf8_name_helpers,test_repo_hygienetest_setup_sh,test_stdout_encodingtests/test_stdout_encoding.py:285: "the default this is about is Windows' own")test_template_text_placeholders,test_update_shape_landingtests/test_shape_doctor.py, shard 0 of 4 (112 of 448 ids)tests/test_shape_doctor.py:10510: "the PowerShell line is printed, and powershell.exe exists, only on a Windows host")tests/test_shape_doctor.py, shard 1 (112 ids)tests/test_shape_doctor.py, shard 2 (112 ids)tests/test_shape_doctor.py, shard 3 (112 ids)The 3 doctor skips are the 3 cases (
family,manifest,pins) oftest_a_template_validators_next_runs_when_pasted_into_powershell, a test markedskipif(os.name != "nt"): one in each of d0, d2 and d3.Totals, summed from the logs:
conftest.pycollects none).tests/test_repo_hygiene.py(101 tests) is in both docs and f21, so over distinct tests: 6353 passed, 10 skipped = 6363.Two notes on the record. The earlier call of 20 files (rc 124 at 530 s) is no result and is not in the table. f11 and f13 were still running when lane 1's LAST STEP stopped the scheduler; both ran to their end, and their
.rcfiles were written by hand (0) after the logs read "260 passed" and "214 passed, 1 skipped": the pytest summaries above, not those two files, are their result.Known limits
GIT_NO_LAZY_FETCH(older than 2.45.0 without the security releases of 2024-05-14, back to 2.39.4) fetches as before; the rows that need the variable skip there, saying so.blob:noneclone that is merely behind, that is every blob of that commit's history, wheregit fetch --allwould add one small pack and cure it too. Measured at this head withprobe_nofetch.py: after the printed cure, 1 pack and 10 files underobjects/; afterfetch --all, 2 packs and 8. Later fetches stay filtered while the remote'spartialclonefilteris set.file://(protocol v2) only, not against GitHub.git fetch --refetchneeds git 2.36 or later; the remediation says so.adopt-project.py planis unchanged (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 handlesplan's own read).Not in this PR
adopt-project.pycallingpin_values_unfetched, and dropping 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's own copy: a follow-up.Lane: openreposhape-1 (openRepoShape-1)
🤖 Generated with Claude Code
Summary by Sourcery
Refuse unreadable partial-clone pin sources without fetching into them, and provide a precise fetch command for recovery.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests: