Skip to content

shape-doctor.py: a conflicted shape pin's NEXT takes whole every copy both sides hold as the standard wrote it at their own pin, and a leg pin put back from before its bump says to commit it and run the pins row's bump-leg.py line (#547) - #586

Merged
brettheap merged 3 commits into
mainfrom
fix/doctor-pin-restore-merges
Oct 15, 2026

Conversation

@brettheap

@brettheap brettheap commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Lane: openreposhape-4 (openRepoShape-4)
Claim: #547 (comment)

What changed and why

Both lines are #460's, in shape-doctor.py's pin_restore.

L2-8. Since #496 (#485), every copy conflicted beside the shape pin went to a person to merge by hand. That included the copies two update-shape.py apply runs to different targets wrote, which neither side had edited. A hand merge that kept both sides of a hunk, then --accept-local, pinned a stale line as the project's own edit.

  • taken_with_the_pin reads each side's stage of contracts/shape-pin.yaml through check_pin (pin_at(..., whole=True)), for the commit it names and its files: rows.
  • A side of a conflicted copy is proved unedited where three things hold:
    • that side's pin has a row for the copy;
    • the copy's stage is byte for byte the standard's own file at THAT side's commit;
    • the two stages hold ONE regular-file mode, 100644 or 100755 (one_regular_mode, fix round 1).
  • The standard's file is mapped by update-shape.py's own source_for, so it is the file check compares. It is read by git show from the checkout the doctor runs from.
  • That checkout is never read when it is a partial clone (partial_clone, fix round 1). There a read would fetch the blob from the promisor remote.
  • A copy proved on both sides is named beside the pin in the one NEXT, git -C <root> checkout --ours -- <pin> <copies>, so the copy taken matches the pin kept.
  • detail["taken_with_the_pin"] lists the copies taken. The why names them as taken whole, and why, beside what it still gives a person.

unreadable_next is touched too, by four lines. The manifest kinds row prints the same NEXT for the same conflicted pin, through unreadable_next.

L2-9. A slip amended into bump-leg.py's own commit leaves the commit before it holding the leg pin for the OLD leg commit. The committed restore puts that pin back, and once it is committed the doctor said DRIFTED, with nothing about the closing step.

  • The NEXT is unchanged.
  • gitlink_left_behind adds to its why, where the pin put back names another commit than the root's gitlink for that leg: both commits, and "commit it, then run the bump-leg.py line the pins row names", with which line keeps the move and which undoes it.
  • The gitlink is read by recorded_gitlink (the index, then HEAD), as the pins row reads it.

Every addition is in why, never in a NEXT (#548's rule).

Fix round 1 (R586's review at 5248e70), in 2ca4044:

  • F1, the mode. A copy whose executable bit the project changed on one side was taken whole as --ours. That dropped the other side's mode, while the why said neither side held an edit.
    • pin_restore now keeps each stage's mode from the ls-files -u -z listing it already reads.
    • taken_with_the_pin proves a copy only where the stages the two sides hold agree on one regular-file mode.
    • A side with no stage is left to the blob read, which proves nothing. So each reason has its own check, and each check its own test.
  • F5, no fetch (R586's O1, the coordinator's decision). Where the standard's checkout is a partial clone, pin_restore proves nothing, every copy stays a person's, and the why says the checkout is a partial clone, so nothing was read from it.
  • N1. taken_with_the_pin returns at once when there is no copy to prove.
  • F2, F3, F4. New tests for a delete/modify conflict and for a side whose pin has no row for the copy. The three composite asserts SonarCloud reported (S9073) are split.

Brett's ruling

"Prove and take" (Brett Heap, 2026-10-14T23:54:30Z), on #547 L2-8:

  • The proof: each side's stage is byte-identical to the standard's own file at the commit THAT side's pin names.
  • Both sides proved: the copy is pure tool output and is taken WHOLE, as the copy that matches the pin the restore keeps. It is not sent to a person.
  • Anything not proved stays the hand merge, exactly as today.
  • The why names the copies taken and the copies left. The NEXT stays ONE command.
  • The standard's files come from the checkout the doctor runs from: "Never fetch, clone or touch the network. If the commit is absent, the copy is unproved."

Tests

New tests/test_doctor_pin_restore_merges.py, 16 tests. Every state is real git: the standard moved on by real commits, the project re-pinned by the real apply, a real merge, and the root under a directory whose name holds a blank and an apostrophe. Every NEXT is run as the reader's shell reads it.

  • (a) both sides pure: both copies are taken whole with the pin, and the root is COMPLIANT once project.yaml is merged and the merge committed;
  • (b) one side edited, parametrized over each side: a hand merge;
  • (c) the same merge as (a), asked of a checkout short of one side's commit or of both: a hand merge;
  • (d) a path the standard lacks at the commit one side's pin names: a hand merge, though that side's stage is the standard's file at another commit;
  • (e) one copy taken and one left, and the why names both;
  • (f) L2-9: the slip amended into the bump gives the sentence; then the NEXT, a commit, and the pins row's keep-the-gitlink line end COMPLIANT. In the control, the slip is a commit of its own, and nothing is added;
  • (g) the NEXT that takes copies, run by sh -c (WINDOWS_SKIP);
  • (h) the naming copy, taken whole: unmerged in the manifest kinds row's restore, and listed in taken_with_the_pin by both rows;
  • (i) the Makefile made executable on one side, parametrized over each side: left to a person, and bootstrap.py still taken.
    • The x bit is set in the INDEX (update-index --chmod=+x), which is the same state on every system.
    • Clearing bootstrap.py's bit would not be: Windows commits it 100644.
  • (j) a delete/modify conflict: the Makefile, at stages 1 and 2, left to a person and still unmerged after the NEXT;
  • (k) the other side's pin restaged without the Makefile's files: row (plumbing, bytes on stdin): the Makefile left, and bootstrap.py still taken;
  • (l) a --filter=blob:none clone of the standard: every copy left, the why's sentence, and C2's two blobs still missing afterwards. rev-list --objects --missing=print checks that, and it never fetches.

Targeted set, Linux, run with test identities and the frvenv on PATH, over 2ca4044's bytes (sha256 of every file checked against the head's blobs):

Mutants, run against the new file at 2ca4044's bytes (R586's runner, copied):

mutant result
M1 take whole without the proof killed: 10 failed
M2a prove only the ours side killed: 4 failed
M2b prove only the theirs side killed: 3 failed
M3a compare each side against the other side's pin killed: 8 failed
M3b compare both sides against the kept pin killed: 8 failed
M4 drop L2-9's sentence killed: 1 failed (f, amended)
M5 drop the unreadable_next change killed: 1 failed (h)
R1 swap the stages killed: 8 failed
R2 drop the files: row requirement killed: 1 failed (k), where it survived at 5248e70
R3 a missing stage counts as proved killed: 1 failed (j), where it survived at 5248e70
R4 one proved copy takes them all killed: 5 failed
R5 a read the standard cannot answer counts as proved killed: 3 failed (c ×2, d)
F1 drop the mode check killed: 2 failed (i ×2)
F5 drop the partial-clone check killed: 1 failed (l)

No existing assertion changed outside the new file.

What stays a hand merge

Exactly what did before, for any copy not proved on BOTH sides:

  • a side whose stage differs from the standard's file at its pin's commit, or is missing (a delete/modify conflict);
  • a mode the two sides disagree on;
  • a side's pin that does not read, or has no files: row for the copy;
  • a pinned commit the standard's checkout the doctor runs from does not hold;
  • a path the standard does not have at that commit;
  • any read that fails.

And every conflicted copy where the standard's checkout is a partial clone. There, a read can fetch, so nothing is read, and the why says so.

None of these proves that the copy holds no edit of the project. Taking such a copy whole is the loss #485 fixed: a copy kept by --accept-local on one side, dropped with the hunks git merged cleanly beside it. The manifest and README.md stay a person's, as before (#267).

Shared files

What this changes in shape-doctor.py:

  • pin_restore (its ls-files -u listing now keeps each stage's mode);
  • pin_at, which gains a whole read used only by pin_restore's new helper;
  • the new helpers taken_with_the_pin, as_the_standard_wrote_it, one_regular_mode, partial_clone and gitlink_left_behind, and the constants REGULAR_FILE_MODES and PARTIAL_CLONE_WHY. Only pin_restore uses them;
  • four lines and one docstring sentence of unreadable_next (above).

git merge-tree --write-tree of 2ca4044 against each head, every head re-read by git ls-remote at 12:41Z and again before the push:

ref head result
origin/main (#580 landed as dd3f23a) dd3f23a clean, tree 58f85167
origin/fix/doctor-validator-verdicts (#557) 65b1189 clean, tree 8d69bbdd
origin/fix/doctor-next-detached-commits (#559) 66a9353 clean, tree 0da21408
origin/l3-548-manifest-kinds (#555) c04ff8a clean, tree 0e4532ec
l3-546-doctor-writes-wording (#580) (landed; branch deleted) in main above
fix/posix-paste-lines (P9) not pushed —
  • source_for, check_pin, root_kind and Upstream are byte-identical in update-shape.py between b4c1f50 and dd3f23a. The proof reads the standard through them, so main's update-shape.py changes do not move it.

Out of scope

  • The v1.0.0 release notes (docs/release-notes/v1.0.0.md:512-513) list L2-8 and L2-9 as known limits. This PR lifts both, so the v1.1.0 notes should drop them. This PR does not edit the v1.0.0 notes.
  • R586's O2 is left as it was: a conflicted copy the KEPT side's pin has no row for is neither taken nor named in the why, as on main.
  • R586's O3 is left as it was: the NEXT's pathspecs are not --literal-pathspecs; today's copy names hold no glob characters.
  • The shape currency row's lazy fetch from a partial-clone standard on a clean root is older than this PR. It goes on Tracking: shape-doctor.py, reporting a repository's compliance #460's checklist.

Closes #547

🤖 Generated with Claude Code

Summary by Sourcery

Safely resolve provably tool-generated shape-copy conflicts and provide complete recovery guidance for restored leg pins.

New Features:

  • Automatically take conflicted shape copies whole with the pin when both sides are proven byte-identical to the standard’s files at their respective pinned commits.
  • Report the same taken-copy resolution in manifest-kind diagnostics and include guidance for leg pins restored ahead of their recorded gitlinks.

Bug Fixes:

  • Prevent unedited tool-generated copies from being incorrectly sent to manual merge, while keeping edited, mode-conflicted, unreadable, missing, and partial-clone cases manual.
  • Clarify the follow-up required when restoring a leg pin whose commit differs from the root gitlink.

Enhancements:

  • Include file modes and partial-clone safety in copy verification, avoiding network-fetching reads and preserving executable-bit correctness.

Tests:

  • Add comprehensive integration coverage for pure, edited, missing, deleted, mode-conflicted, partially cloned, and incompletely pinned copies, plus leg-pin restoration and shell-executed NEXT commands.

…e pin, every copy both sides hold byte for byte as the standard wrote it at that side's own pin, and gives only the rest to a person; and a leg pin put back from before its bump's own commit says, in its `why`, to commit it and run the `pins` row's `bump-leg.py` line (#547)

Both are #460's lines in `pin_restore`, L2-8 and L2-9.

L2-8. Since #496 (#485) every copy conflicted beside the shape pin went to
a person to merge by hand, the copies two `update-shape.py apply` runs to
different targets wrote included, though neither side had edited them; and
a hand merge that kept both sides of a hunk, then `--accept-local`, pinned a
stale line as the project's own edit, so every later upgrade of that copy
was a `both`. Brett Heap ruled "Prove and take" (2026-10-14T23:54:30Z).

The mechanism. `taken_with_the_pin` reads each side's stage of
`contracts/shape-pin.yaml` through `check_pin` (`pin_at(..., whole=True)`)
for the commit it names and its `files:` rows. A side of a conflicted copy
is proved unedited where that side's pin has a row for it and its stage is,
byte for byte, the standard's own file at THAT side's commit: the path
mapped by `update-shape.py`'s own `source_for`, so it is the file `check`
compares, and read by `git show` from the checkout the doctor runs from,
which is how AGENTS.md says to run it. Nothing is fetched or cloned. A copy
proved on both sides is pure tool output: the NEXT names it beside the pin
in the one `git -C <root> checkout --ours -- <pin> <copies>`, so the copy
taken matches the pin kept; `detail["taken_with_the_pin"]` lists it; and
the `why` names it as taken whole, and why, beside the copies, manifest and
README.md it still gives a person. The NEXT stays one command (#548's rule:
every addition goes in `why`).

`unreadable_next` is changed too, by four lines, because the `manifest
kinds` row prints the same NEXT for the same conflicted pin. While the
shape pin does not read nothing calls a copy a copy, so a copy under
`contracts/` that the NEXT now takes whole -- the naming copy, the one a
root keeps there -- came out `project` in that row's `restore`, git not
asked, while its NEXT overwrote the file: the defect #485 fixed for a copy
left to a person. It is now `unmerged`, as that one is, and the row's
`detail` carries the same `taken_with_the_pin`.

What stays a hand merge, exactly as before: a side whose stage differs or
is missing; a side's pin that does not read, or has no row for the copy; a
pinned commit the standard's checkout does not hold; a path the standard
does not have at that commit; any read that fails. None of them proves that
the copy holds no edit of the project, and taking such a copy whole is the
loss #485 fixed: a copy kept by `--accept-local` on one side, dropped by
the other side's copy with the hunks git merged cleanly beside it. The
manifest and README.md stay a person's, as before (#267).

L2-9. A slip amended into `bump-leg.py`'s own commit leaves the commit
before it holding the leg pin for the OLD leg commit, so the `committed`
restore's NEXT, run and committed, left the doctor saying DRIFTED and said
nothing of the step that closes it. The NEXT is unchanged.
`gitlink_left_behind` adds to its `why`, where the pin put back names
another commit than the root's gitlink for that leg, both commits and
"commit it, then run the bump-leg.py line the pins row names", with which
line keeps the move and which undoes it. The gitlink is `recorded_gitlink`'s
(the index, then HEAD), the read the `pins` row makes, so the sentence and
the row it sends the reader to agree.

Tests: new tests/test_doctor_pin_restore_merges.py. Every state is real
git: the standard moved on by real commits, the project re-pinned by the
real `apply`, the merge git's own, the root under a directory whose name
holds a blank and an apostrophe, and every NEXT run as the reader's shell
reads it. (a) both sides pure: both copies taken whole with the pin, and
COMPLIANT once `project.yaml` is merged; (b) one side edited, on either
side: a hand merge; (c) a checkout short of one side's commit, or of both:
a hand merge; (d) a path the standard lacks at the commit one side's pin
names: a hand merge; (e) one copy taken and one left, the `why` naming
both; (f) L2-9's slip amended into the bump: the sentence, then the NEXT, a
commit and the `pins` row's line end COMPLIANT, with a control where the
slip is a commit of its own and the `why` says nothing more; (g) the NEXT
that takes copies, run by `sh -c` (POSIX only); (h) the naming copy taken
whole, `unmerged` and listed as taken in both rows. Mutants, each killed:
taking whole without the proof, proving only the ours side or only the
theirs side, comparing each side against the other side's pin or both
against the kept pin, dropping L2-9's sentence, and dropping the
`unreadable_next` change. No existing assertion changed.

Lane: openreposhape-4 (openRepoShape-4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR makes shape-pin conflict restoration safely take only proven tool-generated copies alongside the pin, keeps all uncertain copies for manual merging, and adds actionable guidance for leg pins restored from before their bump; extensive real-git tests validate behavior, diagnostics, and generated commands.

Sequence diagram for safely restoring conflicted shape copies

sequenceDiagram
    participant Doctor as shape-doctor.py
    participant Git as Git index
    participant Pin as contracts/shape-pin.yaml
    participant Standard as Standard checkout

    Doctor->>Pin: pin_at(:2, whole=True)
    Pin-->>Doctor: commit and files rows
    Doctor->>Pin: pin_at(:3, whole=True)
    Pin-->>Doctor: commit and files rows
    Doctor->>Git: Read stage 2 and stage 3 copy blobs
    Git-->>Doctor: Conflicted copy contents
    Doctor->>Standard: source_for(copy, commit)
    Standard-->>Doctor: Standard blob at each side's commit
    alt Both stages match their pinned standard blobs
        Doctor->>Git: checkout --ours -- pin copy
        Doctor-->>Doctor: Record taken_with_the_pin
    else Any proof fails
        Doctor-->>Doctor: Leave copy for manual merge
    end
Loading

Sequence diagram for restoring a leg pin left behind by a bump

sequenceDiagram
    participant Doctor as shape-doctor.py
    participant History as Pin history
    participant Root as Root index or HEAD
    participant User
    Doctor->>History: Find newest readable pre-break leg pin
    History-->>Doctor: Restored pin commit
    Doctor->>Root: recorded_gitlink(leg path)
    Root-->>Doctor: Current root gitlink
    alt Pin commit differs from root gitlink
        Doctor-->>User: why: commit restored pin, then run the pins row's bump-leg.py line
        User->>User: Commit restored pin
        User->>User: Run bump-leg.py --to gitlink to keep move or --to pin commit to undo
    else Commits match
        Doctor-->>User: No additional gitlink guidance
    end
Loading

Flow diagram for proven-copy conflict restoration

flowchart TD
    A[Conflicted shape pin and copies] --> B[Read both side pins with pin_at]
    B --> C{Each side names the copy and its commit is readable?}
    C -- No --> F[Leave copy for a person]
    C -- Yes --> D[Compare each stage with standard blob via source_for]
    D --> E{Both sides match byte for byte?}
    E -- Yes --> G[Add copy to taken_with_the_pin]
    G --> H[One NEXT checks out pin and proven copies together]
    E -- No --> F
Loading

File-Level Changes

Change Details Files
Automatically takes conflicted shape copies whole only when both merge stages are proven byte-identical to the standard output referenced by their respective pins.
  • Extended pin reads to optionally return validated pin metadata and rows.
  • Added proof helpers that compare staged copy blobs with source files resolved through update-shape.py mappings and each side's pinned commit.
  • Updated the single restore NEXT and diagnostics to include proven copies while preserving hand merges for anything unproven.
  • Added taken and person-merge details, including consistent handling in the manifest-kinds row.
shape-doctor.py
tests/test_doctor_pin_restore_merges.py
Explains the follow-up required when restoring a leg pin from before a bump leaves it inconsistent with the root gitlink.
  • Compares the restored leg pin commit with the recorded index-or-HEAD gitlink.
  • Adds actionable why text directing reviewers to commit the restored pin and run the pins row's bump-leg.py command, distinguishing keep versus undo.
  • Leaves the generated NEXT unchanged and covers amended-bump and control histories.
shape-doctor.py
tests/test_doctor_pin_restore_merges.py
Adds comprehensive real-git coverage for safe copy taking, fallback hand merges, shell execution, diagnostics, and leg-pin repair guidance.
  • Covers pure, edited, partially proven, missing-commit, and missing-source cases.
  • Verifies quoting and execution of generated commands under paths containing spaces and apostrophes.
  • Validates manifest-kinds parity and the amended-versus-separate bump behavior.
tests/test_doctor_pin_restore_merges.py

Assessment against linked issues

Issue Objective Addressed Explanation
#547 For a conflicted shape pin, take a conflicted copy whole alongside the restored pin when both sides’ stages match the standard’s file at the commit named by that side’s pin; leave copies that cannot be proved unedited for a hand merge. ✅
#547 When restoring a leg pin from before a breaking bump commit leaves the pin’s commit out of sync with the root’s gitlink, explain that the restored pin must be committed and direct the user to the pins row’s bump-leg.py command to keep or undo the move. ✅

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

…es, and every conflicted copy where the standard's checkout is a partial clone, stay a person's to merge, so `pin_restore` neither drops a project's executable bit nor fetches to prove a copy; with tests pinning a delete/modify conflict and a side whose pin has no row for the copy (#547)

Fix round 1 for PR #586, from R586's review at 5248e70.

F1, should-fix: the mode. A side was proved unedited by its bytes alone,
so a copy whose executable bit the project changed on one side was taken
whole as `--ours`: the other side's mode was dropped, and the `why` said
neither side held an edit of the project. `pin_restore` now keeps each
stage's mode from the `ls-files -u -z` listing it already reads (`{path:
{stage: mode}}`), and `taken_with_the_pin` proves a copy only where
`one_regular_mode` holds: the stages the two sides hold agree on ONE
regular-file mode, `100644` or `100755`. Where they agree, the restore
keeps the mode both hold. A side with no stage is left to the blob read,
which finds nothing to read and proves nothing, so each reason a copy
stays a person's has its own check and its own test.

F5, the coordinator's decision on R586's O1: no fetch from the new proof
path. In a `--filter` clone of the standard a `git show` of a blob the
clone left out fetches it from the promisor remote, and Brett Heap's
ruling is "never fetch, clone or touch the network". `partial_clone` reads
the checkout's config (`extensions.partialclone`, `remote.*.promisor`),
which fetches and writes nothing; where it names one, `pin_restore` proves
no copy, every copy stays the hand merge, and the `why` says the checkout
is a partial clone, so nothing was read from it. `update-shape.py`'s shared
`Upstream.blob` is untouched: the `shape currency` row's older reach on a
clean root goes on #460's checklist.

N1: `taken_with_the_pin` returns at once with no copy to prove, before it
builds a reader and reads both pins; `pin_restore` runs twice a run.

The docstrings' lists of what stays a hand merge gain the mode the two
sides disagree on and the partial clone.

Tests, in tests/test_doctor_pin_restore_merges.py:
- the Makefile made executable in the index on one side, either side:
  left to a person, bootstrap.py still taken. Set with `update-index
  --chmod=+x`, which is the same state on every system, where clearing
  bootstrap.py's bit is not: Windows commits it `100644`;
- a delete/modify conflict (F2): the Makefile, at stages 1 and 2, left to
  a person and still unmerged after the NEXT;
- the other side's pin restaged without the Makefile's `files:` row (F3),
  through `hash-object -w --stdin` and `update-index -z --index-info`,
  bytes on stdin: the Makefile left, bootstrap.py still taken;
- a `--filter=blob:none` clone of the standard (F5): every copy left, the
  `why`'s sentence, and C2's two blobs still missing afterwards by
  `rev-list --objects --missing=print`, which never fetches.
F4: the three composite asserts SonarCloud reported (S9073) are split.
Mutants, each killed: R586's twelve (M1-M5, R1-R5, R2 and R3 by the new
tests) and two more, dropping the mode check and dropping the
partial-clone check.

Lane: openreposhape-4 (openRepoShape-4)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… merge for ruling 27

A clean merge of main 2bf8fd4 (#555's
squash, on 26c1600), with no hand resolution: git auto-merged
shape-doctor.py and no path was conflicted.

The merge's tree, f9f6e6f, equals the tree
`git merge-tree --write-tree 2ca4044 origin/main` prints (T), and it is the
tree the targeted set passed on in a scratch merge of the predicted #555
squash before #555 landed: 1106 passed, 4 skipped (three PowerShell tests
that need a Windows host and one that needs a case-insensitive disk), over
the PR's own file, the doctor, bump-leg, update-shape and hygiene files.

Lane: openreposhape-4 (openRepoShape-4)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@sourcery-ai

sourcery-ai Bot commented Oct 15, 2026

Copy link
Copy Markdown

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 1 day and 5 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@brettheap
brettheap merged commit 70331aa into main Oct 15, 2026
5 checks passed
@brettheap
brettheap deleted the fix/doctor-pin-restore-merges branch October 15, 2026 18:08
brettheap pushed a commit that referenced this pull request Oct 15, 2026
…arkers of docs/handbook.html set to the merged README.md's section blob, the only conflict

Merges main 70331aa, which holds #597
(Linux-only CI) and lane 4's #586 (shape-doctor.py, #547), so that #588
(#532) lands on a head whose merge base is a main that has touched none of
its files since (ruling 27). README.md, shape-doctor.py, update-shape.py and
tests/test_family_printed_quoting.py auto-merged; docs/handbook.html is the
one conflict.

The conflict is seven one-line hunks, each the marker at the top of a
section that reads README.md § Starting a project in a new organisation.
Main's side carried blob acdf65726ad6433d1d70386eb17f21b015673da0 and
#588's side blob 20dfa48fe4008a2ebf77a4cf1aee232714072f27. Neither names the
merged README.md's section, which now holds both changes, so all seven are
set to 95f06438cf8088e29bba6f56991898f304405f3b: `readme_fingerprint` of
that segment, imported from tests/test_repo_hygiene.py and applied to the
`readme_segments` cut of the merged README.md. The same call over main's
README.md and over #588's reproduces acdf657... and 20dfa48..., so it is the
function the page's check reads. Every other line of the page is as git
merged it, and the page was written as bytes.

Lane: openreposhape-4 (openRepoShape-4)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 15, 2026
Main moved while this merge was being made: a61cf47 (#589) at the
coordinator's request, then 70331aa (#586, shape-doctor.py only) and
2989b4c (#573: family.py, scaffold-project.py, docs/cli.md's scaffold and
family sections, their tests). This merges 2989b4c, the tip at merge
time. 10 commits since 2e6c6fa: 8890f57, fb786c1 (#581), 26c1600,
2bf8fd4, 654c476 (#597), 3f768a6, 024ea55, a61cf47 (#589),
70331aa (#586), 2989b4c (#573).

One conflict, adopt-project.py, the end of `_entry_findings`' return:

  HEAD (#520) added `names_remediation` to the remediation list, so a
  misnamed `adopt_branch:` or `tracking_branch:` prints its remedy;
  main (#589, #449) added `pin_notes` to the notes, so a pin whose digest
  `check` could not recompute is said once at the end.

  Resolved by keeping both, each where its side put it:

            + names_remediation + tracking_remediation + moved_remediation
            + field_remediation,
            pin_notes + notes)

  The two edits touch different elements of the returned tuple, so
  neither changes what the other means: #589's
  `digest_findings, pin_notes = _pin_digest_findings(plan)` and its
  docstring paragraph auto-merged beside #520's `_branch_name_findings`
  call and paragraph.

docs/cli.md auto-merged (#573 changed the scaffold and family sections,
this branch the adopt section); `scripts/render-cli-reference.py --check`
passes on the merge.

Against the new main, each of this branch's six files (adopt-project.py,
docs/cli.md, tests/test_adopt_branch_names.py,
tests/test_adopt_plan_yaml_quoting.py, tests/test_adopt_split_branch.py,
tests/test_adopt_template_values.py) differs by exactly the +/- lines it
did against 2e6c6fa at c598ec3 (adopt-project.py: 399).

Lane: openreposhape-1 (openRepoShape-1)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 15, 2026
…n-source-unread

Merges origin/main 66267f0 so the PR's CI merge ref and the fix that follows
run on current main. The fourteen landings since 2bf8fd4: #597, #578, #593,
#589 (#449), #586, #573 (#537), #575 (#539), #566 (#528), #571, #588, #569
(#538), #582 (#523), #616 and #576 (#441, #442).

Those that touch this PR's files:
- adopt-project.py: #589 (`check` recomputes each pin's digest in the clone
  `plan` read it from: LOCAL_CLONE_RE, _recorded_clone, _why_not_rechecked,
  _recomputed_pin_finding, _pin_digest_findings), #582 (what `plan`,
  `execute` and AGENTS.md say around a run) and #566 (a source's own
  `single-repository.yaml`).
- scaffold-project.py and docs/cli.md: #573, #569 and #576 (the
  `_declaration` docstring: `--pin-source` verifies the first chain link
  alone, which this PR's N3 wording already says).
- tests/test_adopt_pins.py: #589's thirteen cases.
- tests/test_scaffold_leg_path_collisions.py: #573.

This commit holds the textual resolution only. One hunk conflicted, the
`from shape_materialize import (...)` list at adopt-project.py:261. It is
resolved as the union of both sides' changes to the list: main's added
`pin_digest_from_source` (#589), and this PR's added `DIGEST_FROM_CLONE`,
`DIGEST_FROM_GH`, `pin_source_path` and `refuse_an_unread_pin_source` and
its removal of `one_printable_line`, whose last caller this PR deleted and
to which main added none (keeping it is pyflakes' "imported but unused").
pyflakes adopt-project.py is clean on the merge. Every other file merged
without a marker, and `scripts/render-cli-reference.py --check` is ok.

The merge alone does not keep #589 working: this PR's `plan` records a bare
`local-clone`, which names no clone for #589's `check` to read, and `check`
refuses `local-clone (<path>)` as `plan-pin-malformed`. The next commit
reconciles the two.

Lane: openreposhape-2 (openRepoShape-2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap pushed a commit that referenced this pull request Oct 17, 2026
…d A3 (three SHOULDs, four nits), and notes entries for the seven pull requests landed since bf1f099, current to main 2000488 (#563, #683)

Round A4, from lane 2's review (#630 issuecomment-6233033833, READY):
- SHOULD 1: the doctor's exit-3 list names "a validator of its own that
  crashed or could not be run", so the case of one that cannot be given
  its `PYTHONPATH` (shape-doctor.py's EXIT CODES, #278) is listed too:
  README.md:1208-1222, its handbook twin (`doctor`) and the notes' docs
  line. The paragraph re-wraps to one more line:
  tests/readme-cap.d/683.txt is +2, and its text now says which causes
  the EXIT CODES block names and that #742 added the partial clone and
  #701 the naming policy. README.md is 1475 lines, its cap (1422 + 2 + 5
  + 12 + 22 + 10 + 2). Both `Keeping a project's shape current` markers
  move to c728c970: `doctor` text and marker, `current` marker only.
- SHOULD 2: #727's entry names the leg description, whose em dash stopped
  every `execute` against GitHub at the first leg.
- SHOULD 3: #749's entry names `source-ambiguous`, for an `org/repo` that
  is also a directory here.
- Nits: the doctor section's three near-duplicate pairs are merged (#636
  with #737, #632 with #700 and #705, #586 with #736); #718 has its own
  bullet; #634's entry names `git_out`'s spelling for every caller and
  #744's the gate's `--link-source-label` and `--link-remedy`; the header
  comment says 2026-10-17 at main 2000488.

Wave B1, `git log --first-parent bf1f099..2000488`: #706 (#664), #703
(#696), #670 (#646), #682 (#648), #726 (#667), #730 (#666) and #724
(#665), each from its squash commit; #726's and #682's from lane 5's lines
(#630 issuecomment-6227129879), #682's with the unmerged `.gitmodules`
its last round added, and #724's with lane 5's note that v1.0.0's
test-hygiene line on the generator's `.pyc` files closes. The counts (106
pull requests; 116 of 135 milestone issues closed at 11:53Z), "current to
main" and the open list (19) are at main 2000488, still placeholders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

shape-doctor.py pin_restore: a conflicted shape pin sends every copy to a hand merge, and a leg pin broken in its bump commit is restored to the old pin

1 participant