Repository navigation
scripts/shape_materialize.py and adopt-project.py: a --pin-source that is no clone's top level is refused pin-source-not-a-clone, and check finds a recorded one before a partial clone's NOTE, so a folder inside a clone no longer records the digest of part of its tree, or of nothing (#800) - #842
Conversation
…t_pin_source_top_level.py, the failing tests for a --pin-source that is no clone's top level (#800) The tests are written first and have not run yet. The fix in scripts/shape_materialize.py and adopt-project.py, the help texts and docs/cli.md are not written yet. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hat is no clone's top level is refused `pin-source-not-a-clone`, and `check` finds a recorded one before a partial clone's NOTE, so a folder inside a clone no longer records the digest of part of its tree, or of nothing (#800) `tree_digest` runs `git ls-tree -r` in the `--pin-source`, and there git lists only what is under the folder it runs in. On main bf1f099, `plan --pin-source glass/sub` recorded the digest of `sub/` alone, the scaffold's `--dry-run` printed it, and `check` recomputed it and said `plan ok` (#457's L1-82). A folder the commit does not hold, inside a partial clone, recorded the sha256 of nothing and passed `check` with the partial-clone NOTE (L1-04). - `_digest_from_pin_source` asks git, after its two `stat` checks and before any digest, whether the path is a clone's top level (`pin_source_not_top_level`). A working tree's top level, a linked worktree's included, has an empty `rev-parse --show-prefix` outside the git directory; a bare clone's `--absolute-git-dir` is the path itself. Anything else is `pin-source-not-a-clone`, naming the top level git finds ("pass --pin-source <top level>"), or saying git finds none. `pin_digest_from_source` lets that one refusal through instead of wrapping it as `pin-source-unreadable`. - `check` asks a recorded `local-clone (<path>)` the same question first, before `_why_not_rechecked`'s partial-clone NOTE, and finds `plan-pin-source-not-a-clone`, which `execute` refuses before any leg. - Both tools' `--pin-source` help names the refusal; docs/cli.md is regenerated. - tests/test_pin_source_unreadable.py: the `not-a-clone` row and the spelled-reason test now use a `git init` clone that lacks the commit, which is still `pin-source-unreadable` with git's own reason. What a top level digests is unchanged, and scripts/repo_shape.py is not touched. Tests: tests/test_pin_source_top_level.py (48). Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR prevents pins from reading partial or empty subtrees by requiring Sequence diagram for validating a pin source before digestingsequenceDiagram
participant Caller
participant ShapeMaterialize
participant Git
participant TreeDigest
Caller->>ShapeMaterialize: pin_digest_from_source(source, commit, repository)
ShapeMaterialize->>ShapeMaterialize: _digest_from_pin_source(source, commit)
ShapeMaterialize->>ShapeMaterialize: stat(source)
ShapeMaterialize->>Git: rev-parse --is-bare-repository --is-inside-git-dir --show-prefix
Git-->>ShapeMaterialize: clone location and prefix
alt source is clone top level
ShapeMaterialize->>TreeDigest: tree_digest(source, commit)
TreeDigest-->>ShapeMaterialize: whole-tree digest
else source is not clone top level
ShapeMaterialize-->>Caller: Refusal pin-source-not-a-clone
end
Sequence diagram for check finding an invalid recorded pin sourcesequenceDiagram
participant Check
participant AdoptProject
participant ShapeMaterialize
participant Git
Check->>AdoptProject: _pin_digest_findings(plan)
AdoptProject->>ShapeMaterialize: pin_source_not_top_level(recorded_path)
ShapeMaterialize->>Git: rev-parse location query
Git-->>ShapeMaterialize: path is inside clone or not a clone
ShapeMaterialize-->>AdoptProject: why and optional clone top level
AdoptProject-->>Check: FINDING plan-pin-source-not-a-clone
Check-->>Check: refuse execute before any leg
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-1, 2026-10-17T19:15:21Z) Lane 1's independent review of #842 at a2dc2b6: PR-READY Reviewer r800, read-only. I tested a local-only merge, Every probe ran with:
The issue's lines, on main and on the mergeThe product clone
Attack 1: can a source that is not a top level still record a partial or empty digest? No.A digest is partial exactly when git's prefix in the source is not empty. I ran 27 sources through Refused on the merge. Main recorded the digest shown for each.
Accepted on the merge with main's exact output. Each reads
Environment variables:
Case twins. I could not test a case-insensitive filesystem here. From the code: a different-case top level still gets an empty prefix, a different-case folder still gets a non-empty one, and the bare test uses
Attack 2: order and side effects
Attack 3: byte identity for valid sourcesAll 13 valid sources above, main against the merge:
Attack 4: the re-pointed
|
| Row | Reverted | Writer said | r800 got |
|---|---|---|---|
| R1 | the call _refuse_unless_a_clone_top_level(source) |
23 | 23 failed, 106 passed: the 21 refusal cases, the GitHub-order test, the control-character test |
| R3 | found = None in _pin_digest_findings |
7 | 7 failed, 122 passed: every check finding case |
| R5 | the bare branch's samefile test |
4 | 4 failed, 125 passed: the bare folder's three refusals and its finding |
| R10 | git init in lacks-the-commit |
3 | 3 failed, 126 passed: that row through all three runs |
My gate
On merge d665dd47, dirty 0, with git-filter-repo on PATH, using /usr/bin/python3 -m pytest -q -p no:cacheprovider -o tmp_path_retention_policy=failed. The files:
test_pin_source_top_level.pyandtest_pin_source_unreadable.py;test_shape_doctor.py,test_scaffold_leg_path_collisions.pyandtest_cli_reference.py;- extra:
test_agents_md_codes.py,test_adopt_pins.pyandtest_pin_annotated_tag.py.
Result: 906 passed, 3 skipped, 1 xfailed in 774.09s, EXIT=0.
CI
SonarCloud Code Analysis: pass, "0 New issues", 0.0% duplication on new code, 0 annotations.package: pass.tests-git-bash: pass.tests: still pending at review time.- From reading the code I see no S3776 over 15, no S9073, no S5778 and no S1192 in the new code.
Findings, ranked (none blocks)
1. Low (Windows only; CI does not run it): the git-directory reason names a path with forward slashes. In pin_source_not_top_level, the bare branch and the folder branch wrap git's answer in Path, but the git-directory branch keeps the raw string:
gitdir = _rev_parse(source, "--absolute-git-dir")
On Windows, git prints C:/…/openGlass/.git. So the refusal and check's finding would say --pin-source C:\…\openGlass\.git is not a clone's top level: it is inside C:/…/openGlass/.git. The new test's a-git-directory row compares against str(base / "openGlass/.git"), which has backslashes, so it would fail on Windows. The Git Bash job runs only the launcher and installer suites, so nothing in CI sees it.
Fix: gitdir = Path(_rev_parse(source, "--absolute-git-dir")). This changes no byte on POSIX.
2. Low (named in Known limits): a clone git refuses is called "no clone" and "not a clone's top level".
- Repro:
GIT_TEST_ASSUME_DIFFERENT_OWNER=1 adopt-project.py plan … --pin-source <P>/glass. - Main:
REFUSED pin-source-unreadable: <P>/glass could not answer … detected dubious ownership …. - Merge:
REFUSED pin-source-not-a-clone: --pin-source <P>/glass is not a clone's top level: git finds no clone there: … detected dubious ownership …. Its remediation, "pass --pin-source the top level of a clone", points at the path already given. checkof such a recorded top level changes the same way, fromplan-pin-source-unreadabletoplan-pin-source-not-a-clone.- Both versions refuse, so no digest is ever wrong. A later fix could treat only git's "not a git repository" answer as "no clone", and let any other git failure fall through to the reader's
pin-source-unreadableas on main.
3. Nit: three comments in tests/test_pin_source_unreadable.py still say "the not-a-clone refusal" for #339's old case. That now reads like the new code. The writer listed this too. The replacements are below.
Exact text replacements
The PR title and body are true at the head. I checked:
- every How claim;
- the gh order;
- the test counts (21+7+12+4+1+1+2 = 48);
- the four revert rows I re-ran;
- both Known limits.
The body is 14,480 characters and has the Lane line, the Claim line and Closes #800. One note for the landing: the Sourcery bot appended a "## Summary by Sourcery" block after the robot line, so the robot line is no longer the last line. The coordinator may want to strip that block. No sentence of the writer's needs changing.
Optional code text for findings 1 and 3:
scripts/shape_materialize.py:gitdir = _rev_parse(source, "--absolute-git-dir")becomesgitdir = Path(_rev_parse(source, "--absolute-git-dir")).tests/test_pin_source_unreadable.py, module docstring: "with the remediation the not-a-clone refusal already" becomes "with the remediation git's own failure (lacks-the-commit) already".- Same file, line 60:
#: What every refusal is told to do, as the not-a-clone refusal always said it.becomes#: What every refusal is told to do, as git's own failure always said it. - Same file,
unreadabledocstring: "as the not-a-clone refusal always named" becomes "as git's own failure always named".
For the checklist (#457), outside #800
- The question's answers are uneven for git directories. None of these gives a wrong digest; each reads the full
1c1c70…, as on main.super/.git/modules/mods/glass, a submodule's git directory withcore.worktree, is accepted: git answersfalse|false|there.glass.git/worktrees/bare-wt, the admin folder of a bare clone's worktree, is accepted as a bare clone.glass/.gitandglass.git/refsare refused.
- No test holds a worktree of a bare clone (
git -C x.git worktree add), a common layout that passes today. Add it toTOP_LEVELS. - A copied
.gitfolder (non-bare,core.bare=false) is refused as "the git directory of a clone, where git finds no top level", with a remediation that does not fit. Main read the full digest there. - With
GIT_DIRexported and noGIT_WORK_TREE, any--pin-source, even a plain folder, passes and reads the whole tree of the cloneGIT_DIRnames, on main and on the merge alike. - A typed symlink is named by its target. For a symlink to
glass/sub, the refusal names<P>/glass/sub, because_resolved_pin_sourceresolves it before anything else runs. The digest is unaffected. update-shape.py --upstream <path>accepts any folder holding a.gitentry or aHEADfile. Itstree_sha256then callstree_digestwithout--full-tree, whilefiles_atpasses--full-tree. So a folder of a clone that happens to track a file namedHEADwould read a partial digest. This is the same class of bug as adopt-project.py and scaffold-project.py: a--pin-sourcethat is a directory inside a clone records the digest of that directory's subtree alone, or of nothing, soplan,checkand the scaffold pass a pin whosetree_sha256is not the product's tree #800, in another tool.- I confirm the writer's item 1: the root's
validate-pins.py --pin-sourcestill digests a folder as if it were the clone.
Lane 1 takes findings 1 and 3 in a small fix round on the same branch; the same reviewer re-checks the new head.
Lane: openreposhape-1 (openRepoShape-1)
… the git-directory reason names the path as a Path, and three comments say git's own failure, so the refusal no longer spells a git directory with forward slashes on Windows, and #339's comments no longer read as the new code (#800) Review r800's findings 1 and 3, as the lane-1 coordinator took them: - pin_source_not_top_level's git-directory branch wraps git's --absolute-git-dir in Path, as its bare and folder branches do. On Windows git prints forward slashes; this changes no byte on POSIX. - tests/test_pin_source_unreadable.py: three comments that said "the not-a-clone refusal" for #339's old case now say "git's own failure". Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-1, 2026-10-17T19:21:22Z) Lane 1's re-check of #842 at 321b526: PR-READY Linux 1. The diff
2. The printed bytes on Linux are unchanged. I compared the new head with
3. Tests. I made a local-only merge of 4. CI at
My earlier finding 2 is not part of this commit and stays a known limit: a clone git refuses as "dubious ownership" is called "no clone". Nothing else is open. 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 42 minutes by commenting |
Both parents have landed (#747 as 6626a3c, #738 as e62b5e1, each merged above). This brings main up to 7b21059 (#842, #814 and #786's validate.yml change, whose one sentence in templates/family-root/README.md sits apart from this PR's). No conflict. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…trap_after_bump.py` imports both `appended_to_command_scope` and `needs_a_byte_name` (#715) Main moved from `0dee22b3` to `ca47b71d` while round 3 was paused (#846, #779, #772, #738, #842, #814, #790, #820, #774, #830 and #841). `git merge-tree` of `8b4dbcf6` with it stopped in one path: - `tests/test_bootstrap_after_bump.py`, its imports. Main's `aec3b38f` (tests append what they tell git to the command scope) added `appended_to_command_scope` to the `from conftest import (...)` list. This branch added `from test_bootstrap_spelling import needs_a_byte_name` below it (#715, round 1). Both are kept. `scaffold-project.py`, `scripts/bump-leg.py` and `scripts/shape_materialize.py` merged without a conflict. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…take the `--local-remote-dir` that `check_plan` gives, as `test_adopt_pins.py`'s do, so the empty gh log still proves that `check` reads no network for a pin (#798) Main's #842 (bee84d5) added tests/test_pin_source_top_level.py after this branch's last merge. Its two `check` runs passed no `--local-remote-dir` and asserted `gh_calls(gh_env) == ""`; since this PR, `check` without that flag asks GitHub whether each leg repository exists, so the stub logged the two views and CI's `tests` failed 11 cases (7 of `test_check_finds_a_recorded_path_that_is_no_clone_s_top_level`, 4 of `test_check_asks_a_recorded_top_level_as_it_always_did`). Both now run `check_plan(plan, env=gh_env)`, imported from `test_adopt_pins`, as r798's re-verification gave it. No assertion changed. The file gives 48 passed. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…disk or on GitHub, before it makes either, and `check` asks the same, so a code leg that already exists no longer leaves the spec leg made, and a `--local-remote-dir` that cannot hold them no longer ends in a traceback after `plan ok` (#798) (#861) `adopt-project.py execute` made the spec leg's repository first and asked about the code leg's only when it made it (#798). Measured on main `bf1f099b`: - **L1-73.** With the code leg's bare repository already under `--local-remote-dir`, or its name taken on GitHub, `check` said `plan ok`, and `execute` made the spec leg, then was refused at the code leg with `NOTHING has been rolled back` naming the leg it had just made. - **L1-72.** `check --local-remote-dir` said `plan ok` for a directory `execute` could not use: a file at the path ended in a `FileExistsError` traceback, a folder above it this user cannot write in in a `PermissionError` traceback, and a folder this user cannot write in in `a git or gh command failed`. Now: - **`execute` asks about both leg repositories before it makes either,** right before `_create_leg_remotes`, where `leg-remote-exists` was always refused, and refuses everything in their way at once, saying that no leg repository was created and nothing was pushed. - Under `--local-remote-dir` it asks whether each bare repository path is taken (`os.path.lexists`, so a dangling link counts) and whether the directory can be made and written in, by #609's question (`folder_in_the_way`, split out of `folder_cannot_be_made` in `scripts/shape_materialize.py`, which makes nothing). Each case is refused by name, as `leg-remote-exists` or `adopt-folder-cannot-be-made`, with no traceback. - Against GitHub it runs `gh repo view <org>/<P>-spec --json nameWithOwner`, then the code leg's. gh's not-found answer means absent. The name is taken only when the answer's `nameWithOwner` is the name asked, compared without case, so a name GitHub only redirects (a rename or a transfer) is free. Any other failure, or an answer with no `nameWithOwner` to read, is `adopt-remote-unreadable`, naming the command and what gh said. - The `mkdir` of `--local-remote-dir` after the question refuses by name too (`_make_the_remote_dir`). - **`check` reports the same findings,** last among its findings, as `execute` asks them last. Given `--local-remote-dir` it reads no network; without it, it asks GitHub the same two views, and a gh failure is a NOTE there, so `check` stays usable offline. - **#188 N4's notice stays,** for what can still fail after the first create (a private name the token cannot see, a rate limit, Ctrl-C, a full disk), and now says that a re-run is refused as `leg-remote-exists` before either leg is made. - **`tests/gh_guard.py`** answers adopt's exact `--json nameWithOwner` view as a `gh` with no login does, so every `check` in the suite given no `--local-remote-dir` prints the NOTE and passes; scaffold's and family's `--json name` view is still refused. The tests whose own gh stubs or gh-log assertions meet the new call pass `--local-remote-dir` or answer the view; among them is #842's `tests/test_pin_source_top_level.py`, whose two `check` runs now use `check_plan`. - The module docstring, both `--local-remote-dir` helps and docs/cli.md say so. Review: lane 1's independent adversarial review found defects at `99cc97e8`. Fix round 1 (`748c8e6a`) fixed them and merged main; fix round 2 (`70801c91`) cleared SonarCloud's four issues in the new tests; the merge round (`79d518db`) merged main over #825's `cmd_check`, as lane 3 ruled on #473, and took #842's new test into account. The re-check found that head PR-READY. It lands by fast refresh (Brett Heap, 2026-10-15T22:48:35Z). Its reviewed head is green on Linux and SonarCloud. The branch merged main four times and was never rebased; the last merge is update-branch's, clean, giving 25226ab, which is green on SonarCloud and Git Bash. The full suite passed on its tree in lane 1's gate: 244 test files, 15,218 passed and 48 skipped. Closes #798 Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-1 (openRepoShape-1)
Claim: #800 (comment)
What
A
--pin-sourcemust be a clone's top level.adopt-project.py planandscaffold-project.py(its--dry-runand the real run) refuse any other directory by name, andadopt-project.py checkfinds one a plan records. So no pin records the digest of part of a tree, or of nothing.One question, asked before any digest is read.
_digest_from_pin_source(scripts/shape_materialize.py) asks git where the path is, after its twostatchecks (a missing path, a file) and beforetree_digestruns:git rev-parse --show-prefixprints nothing, and the path is not inside the git directory. A linked worktree's top level passes the same way;--is-bare-repositoryistrueand--absolute-git-diris the path itself;pin-source-not-a-clone, which names the top level git finds, or says git finds none.The other answers git can give, in the same refusal:
it is a folder inside the bare clone at /…/glass.git, with the same remediation naming that clone;it is inside /…/glass/.git, the git directory of a clone, where git finds no top level;git finds no clone there: `git rev-parse …` in /…/plain exited 128: fatal: not a git repository ….The last two name no top level, so their remediation is: "pass --pin-source the top level of a clone of the pinned product (
git rev-parse --show-toplevelprints it in that clone's working tree), or drop --pin-source and letgh apiread the forge."checkasks a recorded path the same question, first.checkreads the clone a plan'sdigest_source: local-clone (<path>)names. It now asks this question before_why_not_rechecked, and so before a partial clone's NOTE. A recorded path that is no clone's top level is a new finding,plan-pin-source-not-a-clone, whichexecuterefuses before any leg:The code pair.
pin-source-not-a-clone(plan and scaffold) andplan-pin-source-not-a-clone(check's finding) mirror #651'spin-not-a-commitandplan-pin-not-a-commit. The lane-1 coordinator accepted the pair for this PR. Both tools'--pin-sourcehelp names the refusal, and docs/cli.md is regenerated.Unchanged:
What a top level digests, byte for byte, so existing pins stay valid.
tree_digestin scripts/repo_shape.py, a pinned shape copy. Nothing in that file changes.L1-82, first case (a folder of a clone).
plan, the scaffold's--dry-runand its real run each refuse--pin-source glass/subaspin-source-not-a-clone, exit 2, namingglassas the path to pass.checkof a plan main wrote fromglass/subisplan-pin-source-not-a-clone, exit 1, where main said "digest recomputes" andplan ok. A folder of a partial clone is the same finding, where main called it "a partial clone" and passed it.L1-04 (a folder inside a partial clone that is no clone).
plan --pin-source glass-partial/notacloneispin-source-not-a-clone, namingglass-partial.checkof a plan main wrote from it (tree e3b0c44298fc…, the sha256 of nothing) isplan-pin-source-not-a-clone, exit 1, where main passed it with the partial-clone NOTE.Why (repro on main bf1f099)
The product clone
glassholdsREADME.md,sub/a.txtandlib/b.py; its true digest isd15921cfbaa1…(fromtree_digestat its top level). Every run used a stubghthat logs its calls, and none was logged.adopt-project.py plan --pin openGlass@<head> --pin-source glass/sub: exit 0,pin opensoft/openGlass @ 1cbe383906e2 tree a17948e338f4…, the digest ofsub/alone.adopt-project.py checkon that plan:NOTE … digest recomputes at 1cbe383906e2 in the cloneplanread it from, …/glass/sub.andplan ok, exit 0.scaffold-project.py … --pin-source glass/sub --dry-run: exit 0,pin opensoft/openGlass @ 1cbe383906e2 tree a17948e338f4…,--dry-run: nothing was created.plan … --pin-source glass-partial/notaclone: exit 0,tree e3b0c44298fc….checkon that plan:NOTE … digest NOT rechecked here: the clone at …/glass-partial/notaclone is a partial clone …andplan ok, exit 0.The cause:
tree_digestrunsgit ls-tree -rinside the given path, and without--full-treegit lists only the subtree under the folder it runs in. PR #725 (#651) set this fix aside because it "changes what both tools accept"; what they accepted was a wrong digest.How
pin_source_not_top_level(source)asksgit rev-parse --is-bare-repository --is-inside-git-dir --show-prefixin one call (the prefix printed last, so a folder whose name holds a line break is read whole). Then, where needed, it asks--absolute-git-dir(bare clone, git directory) or--show-toplevel(a folder).repo_shape.git_out. A git that fails givesgit_out's own refusal text, with the folder and git's reason spelled, and that is the "no clone" answer. A pathcwd=cannot enter still raises theOSErrorthatpin-source-unreadablenames, as before._refuse_unless_a_clone_top_levelraisespin-source-not-a-clonefrom it._digest_from_pin_sourcecalls it after its twostatchecks.pin_digest_from_sourcelets that one refusal through instead of wrapping it aspin-source-unreadable.from repo_shape import git_out._not_a_top_level_findingis asked first in_pin_digest_findings, before_why_not_rechecked. It asks only a recorded path that is a directory it can enter, and anything else goes on to the questions after it, as before.PIN_SOURCE_NOT_A_CLONE = "plan-pin-source-not-a-clone".plan-pin-source-unreadableis corrected.--pin-sourcehelp namespin-source-not-a-clone, and docs/cli.md is regenerated (python3 scripts/render-cli-reference.py).not-a-clonerow (nowlacks-the-commit) andtest_git_s_own_reason_names_the_source_spelled_toowere a plain directory. They are now agit initclone that lacks the commit. That is stillpin-source-unreadablewith git's ownls-treereason, and it keeps both tests on the path they were written for (scaffold-project.py:pin_digest_from_sourcecatches onlyRefusal, so a--pin-sourcethat does not exist, is a file, or sits under a parent this user cannot enter ends--dry-runand the real run alike in aFileNotFoundError,NotADirectoryErrororPermissionErrortraceback (exit 1), where a directory that is not a clone is thepin-source-unreadablerefusal (exit 2) with its remediation #339). A directory that is no clone is nowpin-source-not-a-clone, covered by the new file.Where the refusals sit relative to
gh:planrefuses indeclared_pin_values, after--sourceis read. A--source org/repois cloned before any pin is read, so the order allows nothing earlier. With a local--sourceand--pin-source,plancalls noghat all._declaration, which runs before_leg_visibilityandremote_is_emptyaskgh repo view. So against GitHub the dry run refuses before anyghcall; the test's control, a top level, does reachgh.checkandexecuterun nogh.Tests
New: tests/test_pin_source_top_level.py, 48 tests. On main bf1f099, 32 fail. The other 16 are the controls, which pass on main and with this change: the top-level digest, and
check's NOTE, are what they were.test_a_path_that_is_no_clone_s_top_level_is_refused(21). Seven paths:.gitdirectory;Each runs through
plan, the scaffold's--dry-runand the real scaffold, and must be exit 2 with the one refusal, nothing made, and noghcall.test_check_finds_a_recorded_path_that_is_no_clone_s_top_level(7): the same seven, recorded with the digest main'splanread there. Each must beplan-pin-source-not-a-clone, exit 1, with no NOTE for the pin, andexecutemust refuse it before any leg.Controls,
test_a_top_level_is_read_as_it_always_was(12): a top level, a bare clone, a linked worktree and a partial clone, each through all three runs. Each must give the same digest asrepo_shape.tree_digestat the clone's top level.Controls,
test_check_asks_a_recorded_top_level_as_it_always_did(4): "digest recomputes", and the partial clone's NOTE, as before.test_the_scaffold_refuses_before_it_asks_github: against GitHub, noghcall before the refusal; the top-level control does reachgh.test_a_path_holding_a_control_character_is_named_spelled: the folder and the top level are bothrepr'd, and the only line beginningREFUSEDis the tool's own.test_each_tool_names_the_refusal_in_its_help(2).Changed: tests/test_pin_source_unreadable.py. The
not-a-clonerow is nowlacks-the-commit, and the spelled-reason test uses agit initclone (see How).Revert table (each change reverted alone on the merged head a2dc2b6, run over tests/test_pin_source_top_level.py, tests/test_pin_source_unreadable.py and tests/test_cli_reference.py):
_refuse_unless_a_clone_top_level(source)in_digest_from_pin_sourcepin_digest_from_source(the refusal wrapped aspin-source-unreadable)check's question (found = None)checkfinding casecheckfinding; the bare clone's 3 digest controls and itscheckcontrolsamefilecomparisoncheckfinding.gitdirectory's 3 refusals and itscheckfinding--pin-sourcehelpadopt-project.py plan's--pin-sourcehelpgit initin thelacks-the-commitrowgit initin the spelled-reason testBoth modes, on the merged head a2dc2b6 (main 24a977c merged), dirty 0, each shard ending EXIT=0. The 112 files are:
grep -l -E 'adopt-project|adopt_project|\bADOPT\b' tests/*.pynames;--pin-source;They ran in 8 shards with
/usr/bin/python3 -m pytest -q -p no:cacheprovider, a stubghfirst on PATH that logs each call,GH_TOKEN=bogusand an emptyGH_CONFIG_DIR.git-filter-repoon PATH: 7,710 passed, 13 skipped, 1 xfailed.PATH=<stub>:/usr/local/bin:/usr/bin:/bin): 7,331 passed, 392 skipped, 1 xfailed.Windows
CI is Linux only (#597). The new tests write their stand-ins as bytes, read files back with
read_text()/parse_yaml, and create only NTFS-legal names. The control-character test is POSIX-only, as its #339 neighbours are, and the test whose control needs the stubghto run isWINDOWS_SKIP, as #651's are.Not in this PR
tree_digest, a pinned shape copy) and the root'stemplates/assembly-root/scripts/validate-pins.py, which reads its own--pin-sourceand is not asked this question here.--referent-chaindeclaration read from a bare--pin-source(link_pins_from_trees, in scripts/repo_shape.py), which reads aproject.yamlpath and no digest.Known limits
glass/.git) is refused although its digest would have been the whole tree's. This is the brief's rule: the path is not the top level, and the refusal names the git directory rather than guessing its working tree.safe.directory, "dubious ownership") is "git finds no clone there", followed by git's own reason, which says which it was.Closes #800
🤖 Generated with Claude Code
Summary by Sourcery
Require pin sources to identify clone top levels so recorded pin digests always cover the complete pinned tree.
New Features:
Bug Fixes:
Enhancements:
Documentation:
Tests: